我们正在努力摆脱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
Run Code Online (Sandbox Code Playgroud)
以下是对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"]
)
Run Code Online (Sandbox Code Playgroud)
my_summary()
有效,但正如你所看到的,它的实现并不是最漂亮的.以下是主要问题:
DataFrame
和DataFrameGroupBy
不同的方式.因为DataFrame
,我发现的唯一方法是直接调用func(data[var_name_in])
.data[var_name_in].apply(func)
不起作用,因为apply()
在a Series
不减少(不像apply()
a DataFrame
).另一方面,因为DataFrameGroupBy
,我必须使用这种方法:grouped[var_name_in].apply(func)
.这是因为类似的东西func(grouped[var_name_in])
不起作用(没有理由它.)Series
-type参数,需要dot()
乘以和减少它们; 分组函数最终处理SeriesGroupBy
对象并且必须使用*
运算符(对此SO帖子的答案的确认加权平均函数代码.)所以我的问题是:
DataFrameGroupBy
从DataFrame
没有分组的任何变量中获取对象?然后代码路径将减少,因为我们将DataFrameGroupBy
专门处理接口.@ JohnE的回答提供了一种无所事事的方式: groupby(lambda x: True)
.这是他在这篇SO帖子中发现的一种解决方法(顺便提一下,Wes自己的回答是关于a的需要DataFrame.agg()
,它可以起到同样的作用).@ JohnE优秀的解决方案允许我们专门处理类型的对象DataFrameGroupBy
,并立即减少大多数代码路径.我能够进一步减少使用现在可能的功能性噱头,因为我们只有DataFrameGroupBy
实例.基本上,所有函数都是根据需要生成的 - "生成器"(在这里引用,以免与Python生成器表达式混淆)采用两个参数:值列名和权重列名,其中第二个在所有情况下都被忽略,除了wmean
.生成的函数总是应用于整个DataFrameGroupBy
,就像最初的情况一样wmean
,参数是要使用的正确列名.我还np.*
用pandas计算替换了所有实现,以更好地处理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
Run Code Online (Sandbox Code Playgroud)
好吧,这是一个快速解决两个问题的方法(但仍然需要不同的加权平均值函数)。大多数情况下,它使用这里的技巧(归功于@DSM)通过执行groupby(lamda x: True)
. 如果有一个 kwarg 来表示诸如平均值之类的东西的“权重”,那就太好了,但据我所知,没有。显然这里提到了一个基于 numpy 的加权分位数包,但我对此一无所知。顺便说一句,很棒的项目!
(注意,名字大多和你的一样,我只是在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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1325 次 |
最近记录: |