tutorial tablas seleccionar notebook hacer graficos graficar español documentacion datos con como columnas cargar python pandas

python - tablas - ¿Cómo opero un DataFrame con una serie para cada columna?



seleccionar columnas en pandas (2)

Objetivo y Motivación.

He visto este tipo de preguntas varias veces y he visto muchas otras preguntas que involucran algún elemento de esto. Más recientemente, tuve que dedicar un poco de tiempo a explicar este concepto en comentarios mientras buscaba las preguntas y respuestas canónicas adecuadas. No encontré uno y entonces pensé escribir uno.

Esta pregunta generalmente surge con respecto a una operación específica, pero se aplica igualmente a la mayoría de las operaciones aritméticas.

  • ¿Cómo DataFrame una Series de cada columna en un DataFrame ?
  • ¿Cómo agrego una Series de cada columna en un DataFrame ?
  • ¿Cómo multiplico una Series de cada columna en un DataFrame ?
  • ¿Cómo DataFrame una Series de cada columna en un DataFrame ?

La pregunta

Dada una Series s y DataFrame df . ¿Cómo opero cada columna de df con s ?

df = pd.DataFrame( [[1, 2, 3], [4, 5, 6]], index=[0, 1], columns=[''a'', ''b'', ''c''] ) s = pd.Series([3, 14], index=[0, 1])

Cuando intento agregarlos, obtengo todos np.nan

df + s a b c 0 1 0 NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN

Lo que pensé que debería obtener es

a b c 0 4 5 6 1 18 19 20


Por favor, lleve el preámbulo. Es importante abordar primero algunos conceptos de nivel superior. Como mi motivación es compartir conocimientos y enseñar, quise dejar esto lo más claro posible.

Es útil crear un modelo mental de qué son los objetos Series y DataFrame .

Anatomía de una Series

Una Series debe considerarse como un diccionario mejorado. Esto no siempre es una analogía perfecta, pero comenzaremos aquí. También, hay otras analogías que puedes hacer pero estoy apuntando a un diccionario para demostrar el propósito de esta publicación.

index

Estas son las claves a las que podemos hacer referencia para obtener los valores correspondientes. Cuando los elementos del índice son únicos, la comparación con un diccionario se vuelve muy cercana.

values

Estos son los valores correspondientes que están codificados por el índice.

Anatomía de un DataFrame

Un DataFrame debe considerarse como un diccionario de Series o Series de Series . En este caso, las claves son los nombres de columna y los valores son las columnas en sí mismas como objetos de Series . Cada Series acepta compartir el mismo index que es el índice del DataFrame .

columns

Estas son las claves a las que podemos referirnos para obtener las Series correspondientes.

index

Este es el índice que todos los valores de la Series aceptan compartir.

Nota: RE: columns y objetos de index

Son el mismo tipo de cosas. El index un DataFrame se puede usar como las columns otro DataFrame . De hecho, esto sucede cuando haces df.T para obtener una transposición.

values

Esta es una matriz bidimensional que contiene los datos en un DataFrame . La realidad es que los values NO son lo que se almacena dentro del objeto DataFrame . (Bueno, a veces lo es, pero no intentaré describir el administrador de bloques). El punto es que es mejor pensar esto como acceso a una matriz bidimensional de los datos.

Definir datos de muestra

Estos son ejemplos de objetos de pandas.Index que se pueden usar como el index de una Series o DataFrame o se pueden usar como las columns de un DataFrame de DataFrame

idx_lower = pd.Index([*''abcde''], name=''lower'') idx_range = pd.RangeIndex(5, name=''range'')

Estos son objetos pandas.Series muestra que usan los objetos pandas.Index anteriores

s0 = pd.Series(range(10, 15), idx_lower) s1 = pd.Series(range(30, 40, 2), idx_lower) s2 = pd.Series(range(50, 10, -8), idx_range)

Estos son objetos de muestra pandas.DataFrame que usan los objetos pandas.Index anteriores

df0 = pd.DataFrame(100, index=idx_range, columns=idx_lower) df1 = pd.DataFrame( np.arange(np.product(df0.shape)).reshape(df0.shape), index=idx_range, columns=idx_lower )

Series en Series

Cuando se opera en dos Series , la alineación es obvia. Alineas el index de una Series con el index de la otra.

s1 + s0 lower a 40 b 43 c 46 d 49 e 52 dtype: int64

Que es lo mismo que cuando aleatoriamente uno antes de operar. Los índices seguirán alineados.

s1 + s0.sample(frac=1) lower a 40 b 43 c 46 d 49 e 52 dtype: int64

Y NO es el caso cuando, en cambio, opero con los valores de la Series barajada. En este caso, Pandas no tiene el index para alinearse y, por lo tanto, opera desde una posición.

s1 + s0.sample(frac=1).values lower a 42 b 42 c 47 d 50 e 49 dtype: int64

Añadir un escalar

s1 + 1 lower a 31 b 33 c 35 d 37 e 39 dtype: int64

DataFrame en DataFrame

Lo mismo es cierto cuando se opera entre dos DataFrame s
La alineación es obvia y hace lo que pensamos que debería hacer.

df0 + df1 lower a b c d e range 0 100 101 102 103 104 1 105 106 107 108 109 2 110 111 112 113 114 3 115 116 117 118 119 4 120 121 122 123 124

Baraja el segundo DataFrame en ambos ejes. El index y las columns aún se alinearán y nos darán lo mismo.

df0 + df1.sample(frac=1).sample(frac=1, axis=1) lower a b c d e range 0 100 101 102 103 104 1 105 106 107 108 109 2 110 111 112 113 114 3 115 116 117 118 119 4 120 121 122 123 124

Es lo mismo, pero agrega la matriz y no el DataFrame . Ya no está alineado y obtendrá diferentes resultados.

df0 + df1.sample(frac=1).sample(frac=1, axis=1).values lower a b c d e range 0 123 124 121 122 120 1 118 119 116 117 115 2 108 109 106 107 105 3 103 104 101 102 100 4 113 114 111 112 110

Añadir una matriz dimensional. Se alineará con las columnas y se transmitirá a través de filas.

df0 + [*range(2, df0.shape[1] + 2)] lower a b c d e range 0 102 103 104 105 106 1 102 103 104 105 106 2 102 103 104 105 106 3 102 103 104 105 106 4 102 103 104 105 106

Añadir un escalar. Nada para alinearse con las transmisiones a todo.

df0 + 1 lower a b c d e range 0 101 101 101 101 101 1 101 101 101 101 101 2 101 101 101 101 101 3 101 101 101 101 101 4 101 101 101 101 101

DataFrame de DataFrame en Series

Si los DataFrame s deben considerarse como los diccionarios de Series y Series se deben considerar como diccionarios de valores, entonces es natural que cuando se opera entre un DataFrame y Series se deben alinear con sus "claves".

s0: lower a b c d e 10 11 12 13 14 df0: lower a b c d e range 0 100 100 100 100 100 1 100 100 100 100 100 2 100 100 100 100 100 3 100 100 100 100 100 4 100 100 100 100 100

Y cuando operamos, el 10 en s0[''a''] se agrega a la columna completa de df0[''a'']

df0 + s0 lower a b c d e range 0 110 111 112 113 114 1 110 111 112 113 114 2 110 111 112 113 114 3 110 111 112 113 114 4 110 111 112 113 114

Corazón del tema y punto del post.

¿Qué pasa si quiero s2 y df0 ?

s2: df0: | lower a b c d e range | range 0 50 | 0 100 100 100 100 100 1 42 | 1 100 100 100 100 100 2 34 | 2 100 100 100 100 100 3 26 | 3 100 100 100 100 100 4 18 | 4 100 100 100 100 100

Cuando opero, obtengo todos los np.nan tal como se citan en la pregunta

df0 + s2 a b c d e 0 1 2 3 4 range 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

Esto no produce lo que queríamos. Porque Pandas está alineando el index de s2 con las columns de df0 . Las columns del resultado incluyen una unión del index de s2 y las columns de df0 .

Podríamos fingir con una transposición difícil

(df0.T + s2).T lower a b c d e range 0 150 150 150 150 150 1 142 142 142 142 142 2 134 134 134 134 134 3 126 126 126 126 126 4 118 118 118 118 118

Pero resulta que Pandas tiene una mejor solución. Existen métodos de operación que nos permiten pasar un argumento de axis para especificar el eje con el que se alineará.

- sub
+ add
* mul
/ div
** pow

Y así, la respuesta es simple.

df0.add(s2, axis=''index'') lower a b c d e range 0 150 150 150 150 150 1 142 142 142 142 142 2 134 134 134 134 134 3 126 126 126 126 126 4 118 118 118 118 118

Resulta que axis=''index'' es sinónimo de axis=0 .
Como es axis=''columns'' sinónimo de axis=1

df0.add(s2, axis=0) lower a b c d e range 0 150 150 150 150 150 1 142 142 142 142 142 2 134 134 134 134 134 3 126 126 126 126 126 4 118 118 118 118 118

Resto de las operaciones.

df0.sub(s2, axis=0) lower a b c d e range 0 50 50 50 50 50 1 58 58 58 58 58 2 66 66 66 66 66 3 74 74 74 74 74 4 82 82 82 82 82

df0.mul(s2, axis=0) lower a b c d e range 0 5000 5000 5000 5000 5000 1 4200 4200 4200 4200 4200 2 3400 3400 3400 3400 3400 3 2600 2600 2600 2600 2600 4 1800 1800 1800 1800 1800

df0.div(s2, axis=0) lower a b c d e range 0 2.000000 2.000000 2.000000 2.000000 2.000000 1 2.380952 2.380952 2.380952 2.380952 2.380952 2 2.941176 2.941176 2.941176 2.941176 2.941176 3 3.846154 3.846154 3.846154 3.846154 3.846154 4 5.555556 5.555556 5.555556 5.555556 5.555556

df0.pow(1 / s2, axis=0) lower a b c d e range 0 1.096478 1.096478 1.096478 1.096478 1.096478 1 1.115884 1.115884 1.115884 1.115884 1.115884 2 1.145048 1.145048 1.145048 1.145048 1.145048 3 1.193777 1.193777 1.193777 1.193777 1.193777 4 1.291550 1.291550 1.291550 1.291550 1.291550


Prefiero el método mencionado por @piSquared (es decir, df.add (s, axis = 0)), pero otros métodos se apply junto con lambda para realizar una acción en cada columna en el marco de datos:

>>>> df.apply(lambda col: col + s) a b c 0 4 5 6 1 18 19 20

Para aplicar la función lambda a las filas, use axis=1 :

>>> df.T.apply(lambda row: row + s, axis=1) 0 1 a 4 18 b 5 19 c 6 20

Este método podría ser útil cuando la transformación es más compleja, por ejemplo:

df.apply(lambda col: 0.5 * col ** 2 + 2 * s - 3)