我们正在努力摆脱SAS和Python / Pandas.但是,我们遇到的一件事就是创建具有SAS例程灵活性的PROC SUMMARY(AKA PROC MEANS)的替代品.对于非SAS用户:PROC SUMMARY只是一个例程,用于生成一个表,其中包含数据集中“跨所有观察或观察组内的变量的描述性统计”,以解释SAS文档.我们的要求只是完整功能的一小部分 – 输出我们拥有的表:
>能够将不同的统计数据应用于不同的列(现在只是计数,总和,平均值,加权平均值)
>能够处理零到多个分组变量
>能够为加权平均值指定权重变量
我们不会尝试做任何其他事情(任何图形化等)
这是我们迄今为止所拥有的:
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 (
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具有将编程选择的缩减功能应用于单个列的不同方式.对于DataFrame,我发现的唯一方法是直接调用func(data [var_name_in]). data [var_name_in] .apply(func)不起作用,因为Series上的apply()没有减少(与DataFrame上的apply()不同).另一方面,对于DataFrameGroupBy,我必须使用这种方法:分组[var_name_in] .apply(func).这是因为像func(分组[var_name_in])之类的东西不起作用(没有理由应该这样做.)
>加权平均值的特殊处理 – 这是因为它在两列上运行,与所有其他计算不同,它只运行一列;我不知道这是否有帮助.
>两个不同的加权平均函数 – 这是第一个问题的结果.未分组函数具有Series类型参数,需要dot()来乘以和减少它们;分组函数最终处理SeriesGroupBy对象,并且必须使用*运算符(对于加权平均函数代码,确认为the answer to this SO post).
所以我的问题是:
>有没有可以做到这一切的熊猫原生的东西(即抛弃上面的东西并使用它)?
>如果没有,是否有任何修复上述任何问题?
>有任何机会,是否有一些方法可以分组 – 也就是说,从DataFrame获取DataFrameGroupBy对象而不对任何变量进行分组?然后代码路径将减少,因为我们将专门处理DataFrameGroupBy接口.
更新/当前解决方案
@JohnE的回答提供了一种无需分组的方法:groupby(lambda x:True).这是一个解决方法,他发现了in this SO post(顺便提一下,Wes自己说的是需要一个DataFrame.agg(),它可以达到同样的目的). @ JohnE优秀的解决方案允许我们专门处理DataFrameGroupBy类型的对象,并立即减少大部分代码路径.我能够进一步使用现在可能的一些功能性噱头,因为我们只有DataFrameGroupBy实例.基本上,所有函数都是根据需要生成的 – “生成器”(在这里引用,以免与Python生成器表达式混淆)采用两个参数:值列名和权重列名,其中第二个在所有情况下都被忽略,除了wmean.生成的函数总是应用于整个DataFrameGroupBy,就像wmean最初的情况一样,参数是要使用的正确列名.我还用pandas计算替换了所有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
解决方法:
好吧,这是一个快速解决两个问题(但仍然需要一个不同的加权平均函数).大多数情况下,它使用技巧here(归功于@DSM)通过groupby(lamda x:True)绕过你的空组.如果像手段这样的东西上有一个“重量”的kwarg会很棒,但据我所知,没有.显然有一个基于numpy的here加权分位数的包,但我对此一无所知.伟大的项目顺便说一句!
(请注意,名称与您的名称大致相同,我只是在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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。