I am trying to figure out how I can apply cumulative functions to objects. For numbers there are several alternatives like cumsum and cumcount. The
I think you can use cumsum with exception set, then you need first convert to list and then to set. Btw, storing set (C2) or lists of lists (C4) in columns in DataFrame is not recommended.
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]
Turns out this cannot be done.
Continuing on the same sample:
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]
If the column's type is object, the function is never called. And pandas doesn't have an alternative that works on objects. It's the same for rolling().apply().
In some sense, this is a good thing because expanding.apply with a custom function has O(n**2) complexity. With special cases like cumsum, ewma etc, the recursive nature of the operations can decrease the complexity to linear time but in the most general case it should calculate the function for the first n elements, and then for the first n+1 elements and so on. Therefore, especially for a function which is only dependent on the current value and function's previous value, expanding is quite inefficient. Not to mention storing lists or sets in a DataFrame is never a good idea to begin with.
So the answer is: if your data is not numeric and the function is dependent on the previous result and the current element, just use a for loop. It will be more efficient anyway.
well, you can define a custom function
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