Operaciones acumuladas en objetos dtype

Estoy tratando de averiguar cómo puedo aplicar funciones acumulativas a los objetos. Para los números hay varias alternativas como cumsum y cumcount . También hay df.expanding que se puede utilizar con apply . Pero las funciones que paso para apply no funcionan en los objetos.

 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] 

En el dataframe tengo valores enteros, conjuntos, cadenas y listas. Ahora, si bash expanding().apply(sum) tengo la sum acumulada:

 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] 

Mi expectativa era que, dado que la sum está definida en listas y cadenas, obtendría algo como esto:

  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] 

También probé algo como esto:

 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] 

Funciona como espero: el resultado anterior es x y el valor de la fila actual es y . Pero no puedo reducir el uso de x.union(y) , por ejemplo.

Entonces, mi pregunta es: ¿Existen alternativas a la expanding que pueda usar en los objetos? El ejemplo es solo para mostrar que expanding().apply() Apply expanding().apply() no está funcionando en los tipos de objeto. Estoy buscando una solución general que admita la aplicación de funciones a esas dos entradas: el resultado anterior y el elemento actual.

Creo que se puede usar cumsum con el set excepciones, entonces primero necesita convertir a la list y luego a set . Por DataFrame no se recomienda el set almacenamiento ( C2 ) o las lists de lists ( C4 ) en columnas en 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] 

Resulta que esto no se puede hacer.

Continuando en la misma muestra:

 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] 

Si el tipo de la columna es un objeto, la función nunca se llama. Y los pandas no tienen una alternativa que funcione sobre los objetos. Es lo mismo para rolling().apply() .

En cierto sentido, esto es algo bueno porque expanding.apply con una función personalizada tiene una complejidad O (n ** 2). Con casos especiales como cumsum , cumsum , etc., la naturaleza recursiva de las operaciones puede disminuir la complejidad al tiempo lineal, pero en el caso más general debería calcular la función para los primeros n elementos, y luego para los primeros n + 1 elementos, y así en. Por lo tanto, especialmente para una función que solo depende del valor actual y del valor anterior de la función, la expansión es bastante ineficiente. No mencionar que almacenar listas o conjuntos en un DataFrame nunca es una buena idea para empezar.

Entonces, la respuesta es: si sus datos no son numéricos y la función depende del resultado anterior y del elemento actual, solo use un bucle for. Será más eficiente de todos modos.

Bueno, puedes definir una función personalizada.

 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