我试图找出如何将累积函数应用于对象。对于数字,有几种选择,如cumsum
和cumcount
。还有df.expanding可以与apply
一起使用。但是我传递给apply
的函数不适用于对象。
import pandas as pd
df = pd.DataFrame({"C1": [1, 2, 3, 4],
"C2": [{"A"}, {"B"}, {"C"}, {"D"}],
"C3": ["A", "B", "C", "D"],
"C4": [["A"], ["B"], ["C"], ["D"]]})
df
Out:
C1 C2 C3 C4
0 1 {A} A [A]
1 2 {B} B [B]
2 3 {C} C [C]
3 4 {D} D [D]
在数据框中,我有整数值,集合,字符串和列表。现在,如果我尝试expanding().apply(sum)
我有累积总和:
df.expanding().apply(sum)
Out[69]:
C1 C2 C3 C4
0 1.0 {A} A [A]
1 3.0 {B} B [B]
2 6.0 {C} C [C]
3 10.0 {D} D [D]
我的期望是,因为求和是在列表和字符串上定义的,所以我会得到这样的结果:
C1 C2 C3 C4
0 1.0 {A} A [A]
1 3.0 {B} AB [A, B]
2 6.0 {C} ABC [A, B, C]
3 10.0 {D} ABCD [A, B, C, D]
我也尝试过这样的事情:
df.expanding().apply(lambda r: reduce(lambda x, y: x+y**2, r))
Out:
C1 C2 C3 C4
0 1.0 {A} A [A]
1 5.0 {B} B [B]
2 14.0 {C} C [C]
3 30.0 {D} D [D]
它按预期工作:先前的结果是x
,当前行值是y
。但我不能减少使用x.union(y)
,例如。
所以,我的问题是:我可以在物体上使用expanding
的替代品吗?这个例子只是为了表明expanding().apply()
没有处理对象dtypes。我正在寻找支持将函数应用于这两个输入的通用解决方案:前一个结果和当前元素。
我认为你可以使用cumsum
除了set
,然后你需要先转换为list
然后转换为set
。顺便说一下,不建议在set
的列中存储C2
(lists
)或lists
的C4
(DataFrame
)。
print df
C1 C2 C3 C4
0 1 {A} A [A]
1 2 {B} B [B]
2 3 {C} C [C]
3 4 {D} D [D]
print df[['C1','C3','C4']].cumsum()
C1 C3 C4
0 1 A [A]
1 3 AB [A, B]
2 6 ABC [A, B, C]
3 10 ABCD [A, B, C, D]
df['C2'] = df['C2'].apply(list)
df = df.cumsum()
df['C2'] = df['C2'].apply(set)
print df
C1 C2 C3 C4
0 1 {A} A [A]
1 3 {A, B} AB [A, B]
2 6 {A, C, B} ABC [A, B, C]
3 10 {A, C, B, D} ABCD [A, B, C, D]
事实证明这是不可能做到的。
继续使用相同的样本:
def burndowntheworld(ser):
print('Are you sure?')
return ser/0
df.select_dtypes(['object']).expanding().apply(burndowntheworld)
Out:
C2 C3 C4
0 {A} A [A]
1 {B} B [B]
2 {C} C [C]
3 {D} D [D]
如果列的类型是object,则永远不会调用该函数。并且pandas没有可用于对象的替代方案。 rolling().apply()
也是如此。
从某种意义上说,这是一件好事,因为具有自定义函数的expanding.apply
具有O(n ** 2)复杂度。对于像cumsum
,ewma
等特殊情况,操作的递归性质可以降低线性时间的复杂性,但在最一般的情况下,它应该计算前n个元素的函数,然后是前n + 1个元素,所以上。因此,特别是对于仅依赖于当前值和函数的先前值的函数,扩展是非常低效的。更不用说在DataFrame中存储列表或集合从来不是一个好主意。
所以答案是:如果您的数据不是数字且函数依赖于先前的结果和当前元素,则只需使用for循环。无论如何它会更有效率。
好吧,你可以定义一个自定义功能
def custom_cumsum(df):
from functools import reduce
nrows, ncols = df.shape
index, columns = df.index, df.columns
rets = {}
new_col = None
for col in df.columns:
try:
new_col = {col:df.loc[:, col].cumsum()}
except TypeError as e:
if 'set' in str(e):
new_col = {col:[ reduce(set.union, df.loc[:, col][:(i+1)]) for i in range(nrows)]}
rets.update(new_col)
frame = pd.DataFrame(rets, index=index, columns=columns)
return frame