我们正在努力脱离SAS,转而使用Python / Pandas。但是,我们遇到的一件事是创建具有SAS例程灵活性的PROC SUMMARY(AKA PROC MEANS)替代品。对于非SAS用户:PROC SUMMARY只是一个例程,它生成一个表,该表包含数据集中“所有观测值或一组观测值内变量的描述性统计信息”,以解释SAS文档。我们的需求只是全部功能的一小部分- 输出一个我们拥有的表:
PROC SUMMARY
PROC MEANS
我们没有尝试做其他任何事情(任何图形等)。
到目前为止,这里是:
def wmean_ungrouped (d,w): return (d.dot(w)).sum() / w.sum() def wmean_grouped (group, var_name_in, var_name_weight): d = group[var_name_in] w = group[var_name_weight] return (d * w).sum() / w.sum() FUNCS = { "mean" : np.mean , "sum" : np.sum , "count" : np.count_nonzero } def my_summary ( data , var_names_in , var_names_out , var_functions , var_name_weight = None , var_names_group = None ): result = DataFrame() if var_names_group is not None: grouped = data.groupby (var_names_group) for var_name_in, var_name_out, var_function in \ zip(var_names_in,var_names_out,var_functions): if var_function == "wmean": func = lambda x : wmean_grouped (x, var_name_in, var_name_weight) result[var_name_out] = Series(grouped.apply(func)) else: func = FUNCS[var_function] result[var_name_out] = grouped[var_name_in].apply(func) else: for var_name_in, var_name_out, var_function in \ zip(var_names_in,var_names_out,var_functions): if var_function == "wmean": result[var_name_out] = \ Series(wmean_ungrouped(data[var_name_in], data[var_name_weight])) else: func = FUNCS[var_function] result[var_name_out] = Series(func(data[var_name_in])) return result
这是对该my_summary()函数的示例调用:
my_summary()
my_summary ( data=df, var_names_in=["x_1","x_1","x_1","x_1"] , var_names_out=[ "x_1_c","x_1_s","x_1_m","x_1_wm" ] , var_functions=["count","sum","mean","wmean"] , var_name_weight="val_1" , var_names_group=["Region","Category"] )
my_summary()可以,但是如您所见,它的实现并不是最漂亮的。以下是主要问题:
DataFrame
DataFrameGroupBy
func(data[var_name_in])
data[var_name_in].apply(func)
apply()
Series
grouped[var_name_in].apply(func)
func(grouped[var_name_in])
dot()
SeriesGroupBy
*
所以我的问题是:
@JohnE的答案提供了一种按任何内容进行分组的方法: groupby(lambda x: True)。他在此SO帖子中发现了这种变通方法(顺便说一句,Wes亲自回答了对a的需求DataFrame.agg(),这将达到相同的目的)。@JohnE的出色解决方案使我们能够专门处理type的对象DataFrameGroupBy,并立即减少大多数代码路径。由于我们只有DataFrameGroupBy实例,因此我现在可以使用一些可行的功能来进一步减少操作。基本上,所有函数都是根据需要生成的-“生成器”(在此处加引号,以免与Python生成器表达式混淆)具有两个参数:值列名称和权重列名称,在所有情况下都将忽略其中的第二个参数wmean。生成的函数始终应用于整个DataFrameGroupBy,就像最初使用时一样wmean,其参数是要使用的正确列名。我还np.*用熊猫计算替换了所有实现,以更好地处理NaN值。
groupby(lambda x: True)
DataFrame.agg()
wmean
np.*
NaN
除非有熊猫本来的东西可以做到这一点,否则这是我们的解决方案:
FUNC_GENS = { "mean" : lambda y,z : lambda x : x[y].mean(), "sum" : lambda y,z : lambda x : x[y].sum() , "count" : lambda y,z : lambda x : x[y].count() , "wmean" : lambda y,z : lambda x : (x[y] * x[z]).sum() / x[z].sum() } def my_summary ( data , var_names_in , var_names_out , var_functions , var_name_weight = None , var_names_group = None ): result = pd.DataFrame() if var_names_group is None: grouped = data.groupby (lambda x: True) else: grouped = data.groupby (var_names_group) for var_name_in, var_name_out, var_function in \ zip(var_names_in,var_names_out,var_functions): func_gen = FUNC_GENS[var_function] func = func_gen (var_name_in, var_name_weight) result[var_name_out] = grouped.apply(func) return result
在我的原始文章之后发布的熊猫版本现在实现了以下大多数功能:
Series.agg()
NamedAgg``agg()
因此,除加权平均值外,基本上所有其他内容。一个好的当前解决方案在这里。
好吧,这是一个可以解决两个问题的快捷方式(但对于加权均值仍然需要使用其他功能)。通常,它使用此处的技巧(贷记@DSM)来执行操作以避开空的组groupby(lamda x: True)。如果在手段之类的东西上存在“权重”的扭曲,但据我所知没有,那将是很棒的。显然有是提到加权位数包这里基于numpy的,但我不知道这件事。伟大的项目顺便说一句!
groupby(lamda x: True)
(请注意,名称与您的名称基本相同,我只是在wmean_grouped和my_summary中添加了“ 2”,否则您可以使用相同的调用接口)
def wmean_grouped2 (group, var_name_in, var_name_weight): d = group[var_name_in] w = group[var_name_weight] return (d * w).sum() / w.sum() FUNCS = { "mean" : np.mean , "sum" : np.sum , "count" : np.count_nonzero } def my_summary2 ( data , var_names_in , var_names_out , var_functions , var_name_weight = None , var_names_group = None ): result = pd.DataFrame() if var_names_group is None: grouped = data.groupby (lambda x: True) else: grouped = data.groupby (var_names_group) for var_name_in, var_name_out, var_function in \ zip(var_names_in,var_names_out,var_functions): if var_function == "wmean": func = lambda x : wmean_grouped2 (x, var_name_in, var_name_weight) result[var_name_out] = pd.Series(grouped.apply(func)) else: func = FUNCS[var_function] result[var_name_out] = grouped[var_name_in].apply(func) return result