valores una reemplazar nueva filas datos data columna añadir agregar merge pandas multiple-columns return-type

merge - una - pandas python



¿Aplica la función pandas a la columna para crear múltiples columnas nuevas? (7)

A partir de la respuesta del usuario1827356, puede hacer la asignación en una sola pasada usando df.merge :

df.merge(df.textcol.apply(lambda s: pd.Series({''feature1'':s+1, ''feature2'':s-1})), left_index=True, right_index=True) textcol feature1 feature2 0 0.772692 1.772692 -0.227308 1 0.857210 1.857210 -0.142790 2 0.065639 1.065639 -0.934361 3 0.819160 1.819160 -0.180840 4 0.088212 1.088212 -0.911788

Cómo hacer esto en pandas:

Tengo una función extract_text_features en una sola columna de texto, devolviendo múltiples columnas de salida. Específicamente, la función devuelve 6 valores.

La función funciona, sin embargo, no parece haber ningún tipo de retorno adecuado (pandas DataFrame / numpy array / Python list) para que la salida se pueda asignar correctamente df.ix[: ,10:16] = df.textcol.map(extract_text_features)

¿Entonces creo que necesito volver a iterar con df.iterrows() , de acuerdo con this ?

ACTUALIZACIÓN: la df.iterrows() con df.iterrows() es al menos 20 veces más lenta, por lo que me df.iterrows() y df.iterrows() la función en seis llamadas .map(lambda ...) distintas.


Esta es la forma correcta y más fácil de lograr esto en el 95% de los casos de uso:

>>> df = pd.DataFrame(zip(*[range(10)]), columns=[''num'']) >>> df num 0 0 1 1 2 2 3 3 4 4 5 5 >>> def example(x): ... x[''p1''] = x[''num'']**2 ... x[''p2''] = x[''num'']**3 ... x[''p3''] = x[''num'']**4 ... return x >>> df = df.apply(example, axis=1) >>> df num p1 p2 p3 0 0 0 0 0 1 1 1 1 1 2 2 4 8 16 3 3 9 27 81 4 4 16 64 256


Esto es lo que hice en el pasado

df = pd.DataFrame({''textcol'' : np.random.rand(5)}) df textcol 0 0.626524 1 0.119967 2 0.803650 3 0.100880 4 0.017859 df.textcol.apply(lambda s: pd.Series({''feature1'':s+1, ''feature2'':s-1})) feature1 feature2 0 1.626524 -0.373476 1 1.119967 -0.880033 2 1.803650 -0.196350 3 1.100880 -0.899120 4 1.017859 -0.982141

Edición para completar

pd.concat([df, df.textcol.apply(lambda s: pd.Series({''feature1'':s+1, ''feature2'':s-1}))], axis=1) textcol feature1 feature2 0 0.626524 1.626524 -0.373476 1 0.119967 1.119967 -0.880033 2 0.803650 1.803650 -0.196350 3 0.100880 1.100880 -0.899120 4 0.017859 1.017859 -0.982141


He buscado varias formas de hacerlo y el método que se muestra aquí (devolver una serie de pandas) no parece ser más eficiente.

Si comenzamos con una gran cantidad de datos de datos aleatorios:

# Setup a dataframe of random numbers and create a df = pd.DataFrame(np.random.randn(10000,3),columns=list(''ABC'')) df[''D''] = df.apply(lambda r: '':''.join(map(str, (r.A, r.B, r.C))), axis=1) columns = ''new_a'', ''new_b'', ''new_c''

El ejemplo que se muestra aquí:

# Create the dataframe by returning a series def method_b(v): return pd.Series({k: v for k, v in zip(columns, v.split('':''))}) %timeit -n10 -r3 df.D.apply(method_b)

10 loops, lo mejor de 3: 2.77 s por ciclo

Un método alternativo:

# Create a dataframe from a series of tuples def method_a(v): return v.split('':'') %timeit -n10 -r3 pd.DataFrame(df.D.apply(method_a).tolist(), columns=columns)

10 loops, lo mejor de 3: 8,85 ms por ciclo

Según mis cálculos, es mucho más eficiente tomar una serie de tuplas y luego convertirlas en un DataFrame. Sin embargo, estaría interesado en escuchar el pensamiento de la gente si hay un error en mi trabajo.


La solución aceptada será extremadamente lenta para muchos datos. La solución con el mayor número de upvotes es un poco difícil de leer y también lenta con datos numéricos. Si cada nueva columna puede calcularse independientemente de las demás, simplemente asignaría cada una de ellas directamente sin utilizar apply .

Ejemplo con datos de caracteres falsos

Crear 100.000 cadenas en un DataFrame

df = pd.DataFrame(np.random.choice([''he jumped'', ''she ran'', ''they hiked''], size=100000, replace=True), columns=[''words'']) df.head() words 0 she ran 1 she ran 2 they hiked 3 they hiked 4 they hiked

Digamos que queríamos extraer algunas características del texto como se hizo en la pregunta original. Por ejemplo, vamos a extraer el primer carácter, contar la aparición de la letra "e" y ponerle mayúscula a la frase.

df[''first''] = df[''words''].str[0] df[''count_e''] = df[''words''].str.count(''e'') df[''cap''] = df[''words''].str.capitalize() df.head() words first count_e cap 0 she ran s 1 She ran 1 she ran s 1 She ran 2 they hiked t 2 They hiked 3 they hiked t 2 They hiked 4 they hiked t 2 They hiked

Tiempos

%%timeit df[''first''] = df[''words''].str[0] df[''count_e''] = df[''words''].str.count(''e'') df[''cap''] = df[''words''].str.capitalize() 127 ms ± 585 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) def extract_text_features(x): return x[0], x.count(''e''), x.capitalize() %timeit df[''first''], df[''count_e''], df[''cap''] = zip(*df[''words''].apply(extract_text_features)) 101 ms ± 2.96 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Sorprendentemente, puede obtener un mejor rendimiento al recorrer cada valor

%%timeit a,b,c = [], [], [] for s in df[''words'']: a.append(s[0]), b.append(s.count(''e'')), c.append(s.capitalize()) df[''first''] = a df[''count_e''] = b df[''cap''] = c 79.1 ms ± 294 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Otro ejemplo con datos numéricos falsos

Crea 1 millón de números aleatorios y prueba la función de powers desde arriba.

df = pd.DataFrame(np.random.rand(1000000), columns=[''num'']) def powers(x): return x, x**2, x**3, x**4, x**5, x**6 %%timeit df[''p1''], df[''p2''], df[''p3''], df[''p4''], df[''p5''], df[''p6''] = / zip(*df[''num''].map(powers)) 1.35 s ± 83.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Asignar cada columna es 25 veces más rápido y muy legible:

%%timeit df[''p1''] = df[''num''] ** 1 df[''p2''] = df[''num''] ** 2 df[''p3''] = df[''num''] ** 3 df[''p4''] = df[''num''] ** 4 df[''p5''] = df[''num''] ** 5 df[''p6''] = df[''num''] ** 6 51.6 ms ± 1.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Hice una respuesta similar con más detalles aquí sobre por qué apply normalmente no es el camino a seguir.


Normalmente hago esto usando zip :

>>> df = pd.DataFrame([[i] for i in range(10)], columns=[''num'']) >>> df num 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 >>> def powers(x): >>> return x, x**2, x**3, x**4, x**5, x**6 >>> df[''p1''], df[''p2''], df[''p3''], df[''p4''], df[''p5''], df[''p6''] = / >>> zip(*df[''num''].map(powers)) >>> df num p1 p2 p3 p4 p5 p6 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 4 8 16 32 64 3 3 3 9 27 81 243 729 4 4 4 16 64 256 1024 4096 5 5 5 25 125 625 3125 15625 6 6 6 36 216 1296 7776 46656 7 7 7 49 343 2401 16807 117649 8 8 8 64 512 4096 32768 262144 9 9 9 81 729 6561 59049 531441


Resumen: si solo quiere crear algunas columnas, use df[[''new_col1'',''new_col2'']] = df[[''data1'',''data2'']].apply( function_of_your_choosing(x), axis=1)

Para esta solución, el número de columnas nuevas que está creando debe ser igual al número de columnas que utiliza como entrada para la función .apply (). Si quieres hacer otra cosa, echa un vistazo a las otras respuestas.

Detalles Digamos que tiene dataframe de dos columnas. La primera columna es la altura de una persona cuando tiene 10; el segundo es la altura de dicha persona cuando tienen 20 años.

Supongamos que necesita calcular tanto la media de las alturas de cada persona como la suma de las alturas de cada persona. Eso es dos valores por cada fila.

Puede hacerlo a través de la siguiente función que se aplicará próximamente:

def mean_and_sum(x): """ Calculates the mean and sum of two heights. Parameters: :x -- the values in the row this function is applied to. Could also work on a list or a tuple. """ sum=x[0]+x[1] mean=sum/2 return [mean,sum]

Puede utilizar esta función de esta manera:

df[[''height_at_age_10'',''height_at_age_20'']].apply(mean_and_sum(x),axis=1)

(Para ser claros: esta función de aplicación toma los valores de cada fila en el marco de datos subconjunto y devuelve una lista).

Sin embargo, si haces esto:

df[''Mean_&_Sum''] = df[[''height_at_age_10'',''height_at_age_20'']].apply(mean_and_sum(x),axis=1)

creará una nueva columna que contenga las listas [media, suma], que presumiblemente querría evitar, porque eso requeriría otro Lambda / Apply.

En cambio, desea dividir cada valor en su propia columna. Para hacer esto, puede crear dos columnas a la vez:

df[[''Mean'',''Sum'']] = df[[''height_at_age_10'',''height_at_age_20'']] .apply(mean_and_sum(x),axis=1)