He visto muchas preguntas sobre tablas dinámicas. Incluso si no saben que están preguntando sobre tablas dinámicas, por lo general lo hacen. Es virtualmente imposible escribir una pregunta canónica y una respuesta que abarque todos los aspectos del pivoteo...
... Pero voy a intentarlo.
El problema con las preguntas y respuestas existentes es que, a menudo, la pregunta se centra en un matiz que el OP tiene problemas para generalizar con el fin de utilizar varias de las buenas respuestas existentes. Sin embargo, ninguna de las respuestas intenta dar una explicación completa (porque es una tarea desalentadora)
Mira algunos ejemplos de mi Búsqueda de Google
pd.DataFrame.pivot
Entonces, cada vez que alguien busca pivot
, obtiene resultados esporádicos que probablemente no respondan a su pregunta específica.
Puede notar que nombré de manera llamativa mis columnas y los valores de columna relevantes para que se correspondan con la forma en que voy a pivotar en las respuestas a continuación.
import numpy as np import pandas as pd from numpy.core.defchararray import add np.random.seed([3,1415]) n = 20 cols = np.array(['key', 'row', 'item', 'col']) arr1 = (np.random.randint(5, size=(n, 4)) // [2, 1, 2, 1]).astype(str) df = pd.DataFrame( add(cols, arr1), columns=cols ).join( pd.DataFrame(np.random.rand(n, 2).round(2)).add_prefix('val') ) print(df) key row item col val0 val1 0 key0 row3 item1 col3 0.81 0.04 1 key1 row2 item1 col2 0.44 0.07 2 key1 row0 item1 col0 0.77 0.01 3 key0 row4 item0 col2 0.15 0.59 4 key1 row0 item2 col1 0.81 0.64 5 key1 row2 item2 col4 0.13 0.88 6 key2 row4 item1 col3 0.88 0.39 7 key1 row4 item1 col1 0.10 0.07 8 key1 row0 item2 col4 0.65 0.02 9 key1 row2 item0 col2 0.35 0.61 10 key2 row0 item2 col1 0.40 0.85 11 key2 row4 item1 col2 0.64 0.25 12 key0 row2 item2 col3 0.50 0.44 13 key0 row4 item1 col4 0.24 0.46 14 key1 row3 item2 col3 0.28 0.11 15 key0 row3 item1 col1 0.31 0.23 16 key0 row0 item2 col3 0.86 0.01 17 key0 row4 item0 col3 0.64 0.21 18 key2 row2 item2 col0 0.13 0.45 19 key0 row2 item0 col4 0.37 0.70
¿Por qué obtengo ValueError: Index contains duplicate entries, cannot reshape
¿Cómo giro df
de modo que los valores de col
sean columnas, los valores de row
sean el índice y la media de val0
sean los valores?
col col0 col1 col2 col3 col4 row row0 0.77 0.605 NaN 0.860 0.65 row2 0.13 NaN 0.395 0.500 0.25 row3 NaN 0.310 NaN 0.545 NaN row4 NaN 0.100 0.395 0.760 0.24
¿Cómo giro df
de modo que los valores de col
sean columnas, los valores de row
sean el índice, la media de val0
sean los valores y los valores faltantes sean 0
?
col col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 row2 0.13 0.000 0.395 0.500 0.25 row3 0.00 0.310 0.000 0.545 0.00 row4 0.00 0.100 0.395 0.760 0.24
¿Puedo obtener algo que no sea mean
, como tal vez la sum
?
col col0 col1 col2 col3 col4 row row0 0.77 1.21 0.00 0.86 0.65 row2 0.13 0.00 0.79 0.50 0.50 row3 0.00 0.31 0.00 1.09 0.00 row4 0.00 0.10 0.79 1.52 0.24
¿Puedo hacer más de una agregación a la vez?
sum mean col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 0.77 1.21 0.00 0.86 0.65 0.77 0.605 0.000 0.860 0.65 row2 0.13 0.00 0.79 0.50 0.50 0.13 0.000 0.395 0.500 0.25 row3 0.00 0.31 0.00 1.09 0.00 0.00 0.310 0.000 0.545 0.00 row4 0.00 0.10 0.79 1.52 0.24 0.00 0.100 0.395 0.760 0.24
¿Puedo agregar varias columnas de valores?
val0 val1 col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 0.01 0.745 0.00 0.010 0.02 row2 0.13 0.000 0.395 0.500 0.25 0.45 0.000 0.34 0.440 0.79 row3 0.00 0.310 0.000 0.545 0.00 0.00 0.230 0.00 0.075 0.00 row4 0.00 0.100 0.395 0.760 0.24 0.00 0.070 0.42 0.300 0.46
¿Se puede subdividir por varias columnas?
item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 row row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.605 0.86 0.65 row2 0.35 0.00 0.37 0.00 0.00 0.44 0.00 0.00 0.13 0.000 0.50 0.13 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.000 0.28 0.00 row4 0.15 0.64 0.00 0.00 0.10 0.64 0.88 0.24 0.00 0.000 0.00 0.00
O
item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 key row key0 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.86 0.00 row2 0.00 0.00 0.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.50 0.00 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.00 0.00 0.00 row4 0.15 0.64 0.00 0.00 0.00 0.00 0.00 0.24 0.00 0.00 0.00 0.00 key1 row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.81 0.00 0.65 row2 0.35 0.00 0.00 0.00 0.00 0.44 0.00 0.00 0.00 0.00 0.00 0.13 row3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.28 0.00 row4 0.00 0.00 0.00 0.00 0.10 0.00 0.00 0.00 0.00 0.00 0.00 0.00 key2 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.40 0.00 0.00 row2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.13 0.00 0.00 0.00 row4 0.00 0.00 0.00 0.00 0.00 0.64 0.88 0.00 0.00 0.00 0.00 0.00
¿Puedo agregar la frecuencia en la que la columna y las filas aparecen juntas, también conocida como "tabulación cruzada"?
col col0 col1 col2 col3 col4 row row0 1 2 0 1 1 row2 1 0 2 1 2 row3 0 1 0 2 0 row4 0 1 2 2 1
¿Cómo convierto un DataFrame de largo a ancho girando SOLO en dos columnas? Dado,
np.random.seed([3, 1415]) df2 = pd.DataFrame({'A': list('aaaabbbc'), 'B': np.random.choice(15, 8)}) df2 AB 0 a 0 1 a 11 2 a 2 3 a 11 4 b 10 5 b 10 6 b 14 7 c 7
Lo esperado debería ser algo como
abc 0 0.0 10.0 7.0 1 11.0 10.0 NaN 2 2.0 14.0 NaN 3 11.0 NaN NaN
¿Cómo aplano el índice múltiple a un solo índice después del pivot
?
Desde
1 2 1 1 2 a 2 1 1 b 2 1 0 c 1 0 0
Para
1|1 2|1 2|2 a 2 1 1 b 2 1 0 c 1 0 0
Para comprender mejor cómo funciona el pivote , puede consultar el ejemplo de la documentación de Pandas:
df = pd.DataFrame({ 'foo': ['one', 'one', 'one', 'two', 'two', 'two'], 'bar': ['A', 'B', 'C', 'A', 'B', 'C'], 'baz': [1, 2, 3, 4, 5, 6], 'zoo': ['x', 'y', 'z', 'q', 'w', 't'] })
Tabla de entrada:
foo bar baz zoo 0 one A 1 x 1 one B 2 y 2 one C 3 z 3 two A 4 q 4 two B 5 w 5 two C 6 t
pivote :
pd.pivot( data=df, index='foo', # Column to use to make new frame's index. If None, uses existing index. columns='bar', # Column to use to make new frame's columns. values='baz' # Column(s) to use for populating new frame's values. )
Tabla de salida:
bar ABC foo one 1 2 3 two 4 5 6
Para extender la respuesta de @piRSquared otra versión de la Pregunta 10
Marco de datos:
d = data = {'A': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 5}, 'B': {0: 'a', 1: 'b', 2: 'c', 3: 'a', 4: 'b', 5: 'a', 6: 'c'}} df = pd.DataFrame(d) AB 0 1 a 1 1 b 2 1 c 3 2 a 4 2 b 5 3 a 6 5 c
Producción:
0 1 2 A 1 abc 2 ab None 3 a None None 5 c None None
Uso df.groupby
y pd.Series.tolist
t = df.groupby('A')['B'].apply(list) out = pd.DataFrame(t.tolist(),index=t.index) out 0 1 2 A 1 abc 2 ab None 3 a None None 5 c None None
O una alternativa mucho mejor usando pd.pivot_table
con df.squeeze.
t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze() out = pd.DataFrame(t.tolist(),index=t.index)
Empezamos respondiendo a la primera pregunta:
¿Por qué obtengo
ValueError: Index contains duplicate entries, cannot reshape
Esto ocurre porque pandas está intentando volver a indexar una columns
o un objeto de index
con entradas duplicadas. Hay varios métodos para usar que pueden realizar un pivote. Algunos de ellos no se adaptan bien cuando hay duplicados de las teclas en las que se le pide que pivote. Por ejemplo. Considere pd.DataFrame.pivot
. Sé que hay entradas duplicadas que comparten los valores de row
y col
:
df.duplicated(['row', 'col']).any() True
Así que cuando pivot
usando
df.pivot(index='row', columns='col', values='val0')
Me sale el error mencionado anteriormente. De hecho, recibo el mismo error cuando intento realizar la misma tarea con:
df.set_index(['row', 'col'])['val0'].unstack()
Aquí hay una lista de modismos que podemos usar para pivotar
pd.DataFrame.groupby
+ pd.DataFrame.unstack
unstack
los niveles que desea que estén en el índice de la columna.groupby
con una API más intuitiva. Para muchas personas, este es el enfoque preferido. Y es el enfoque previsto por los desarrolladores. pd.DataFrame.set_index
+ pd.DataFrame.unstack
groupby
, especificamos todas las columnas que eventualmente serán niveles de fila o columna y establecemos que sean el índice. A continuación unstack
los niveles que queramos en las columnas. Si los niveles de índice o los niveles de columna restantes no son únicos, este método fallará.set_index
en que comparte la limitación de clave duplicada. La API también es muy limitada. Solo toma valores escalares para index
, columns
, values
.pivot_table
en el que seleccionamos filas, columnas y valores sobre los cuales pivotar. Sin embargo, no podemos agregar y si las filas o las columnas no son únicas, este método fallará.pivot_table
y en su forma más pura es la forma más intuitiva de realizar varias tareas. pd.get_dummies
+ pd.DataFrame.dot
Lo que voy a hacer para cada respuesta y pregunta posterior es responderla usando pd.DataFrame.pivot_table
. A continuación, proporcionaré alternativas para realizar la misma tarea.
¿Cómo giro
df
de modo que los valores decol
sean columnas, los valores derow
sean el índice, la media deval0
sean los valores y los valores faltantes sean0
?
fill_value
no está configurado de forma predeterminada. Tiendo a configurarlo apropiadamente. En este caso lo puse a 0
. Observe que omití la pregunta 2 porque es lo mismo que esta respuesta sin el valor de fill_value
aggfunc='mean'
es el predeterminado y no tuve que configurarlo. Lo incluí para ser explícito.
df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc='mean') col col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 row2 0.13 0.000 0.395 0.500 0.25 row3 0.00 0.310 0.000 0.545 0.00 row4 0.00 0.100 0.395 0.760 0.24
df.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='mean').fillna(0)
¿Puedo obtener algo que no sea
mean
, como tal vez lasum
?
df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc='sum') col col0 col1 col2 col3 col4 row row0 0.77 1.21 0.00 0.86 0.65 row2 0.13 0.00 0.79 0.50 0.50 row3 0.00 0.31 0.00 1.09 0.00 row4 0.00 0.10 0.79 1.52 0.24
df.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc='sum').fillna(0)
¿Puedo hacer más de una agregación a la vez?
Tenga en cuenta que para pivot_table
y crosstab
necesitaba pasar la lista de invocables. Por otro lado, groupby.agg
puede tomar cadenas para un número limitado de funciones especiales. groupby.agg
también habría tomado las mismas llamadas que pasamos a los demás, pero a menudo es más eficiente aprovechar los nombres de las funciones de cadena, ya que se pueden obtener eficiencias.
df.pivot_table( values='val0', index='row', columns='col', fill_value=0, aggfunc=[np.size, np.mean]) size mean col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 1 2 0 1 1 0.77 0.605 0.000 0.860 0.65 row2 1 0 2 1 2 0.13 0.000 0.395 0.500 0.25 row3 0 1 0 2 0 0.00 0.310 0.000 0.545 0.00 row4 0 1 2 2 1 0.00 0.100 0.395 0.760 0.24
df.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)
pd.crosstab( index=df['row'], columns=df['col'], values=df['val0'], aggfunc=[np.size, np.mean]).fillna(0, downcast='infer')
¿Puedo agregar varias columnas de valores?
pd.DataFrame.pivot_table
pasamos values=['val0', 'val1']
pero podríamos haberlo dejado completamente
df.pivot_table( values=['val0', 'val1'], index='row', columns='col', fill_value=0, aggfunc='mean') val0 val1 col col0 col1 col2 col3 col4 col0 col1 col2 col3 col4 row row0 0.77 0.605 0.000 0.860 0.65 0.01 0.745 0.00 0.010 0.02 row2 0.13 0.000 0.395 0.500 0.25 0.45 0.000 0.34 0.440 0.79 row3 0.00 0.310 0.000 0.545 0.00 0.00 0.230 0.00 0.075 0.00 row4 0.00 0.100 0.395 0.760 0.24 0.00 0.070 0.42 0.300 0.46
df.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
¿Se puede subdividir por varias columnas?
df.pivot_table( values='val0', index='row', columns=['item', 'col'], fill_value=0, aggfunc='mean') item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 row row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.605 0.86 0.65 row2 0.35 0.00 0.37 0.00 0.00 0.44 0.00 0.00 0.13 0.000 0.50 0.13 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.000 0.28 0.00 row4 0.15 0.64 0.00 0.00 0.10 0.64 0.88 0.24 0.00 0.000 0.00 0.00
df.groupby( ['row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
¿Se puede subdividir por varias columnas?
df.pivot_table( values='val0', index=['key', 'row'], columns=['item', 'col'], fill_value=0, aggfunc='mean') item item0 item1 item2 col col2 col3 col4 col0 col1 col2 col3 col4 col0 col1 col3 col4 key row key0 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.86 0.00 row2 0.00 0.00 0.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.50 0.00 row3 0.00 0.00 0.00 0.00 0.31 0.00 0.81 0.00 0.00 0.00 0.00 0.00 row4 0.15 0.64 0.00 0.00 0.00 0.00 0.00 0.24 0.00 0.00 0.00 0.00 key1 row0 0.00 0.00 0.00 0.77 0.00 0.00 0.00 0.00 0.00 0.81 0.00 0.65 row2 0.35 0.00 0.00 0.00 0.00 0.44 0.00 0.00 0.00 0.00 0.00 0.13 row3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.28 0.00 row4 0.00 0.00 0.00 0.00 0.10 0.00 0.00 0.00 0.00 0.00 0.00 0.00 key2 row0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.40 0.00 0.00 row2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.13 0.00 0.00 0.00 row4 0.00 0.00 0.00 0.00 0.00 0.64 0.88 0.00 0.00 0.00 0.00 0.00
df.groupby( ['key', 'row', 'item', 'col'] )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
pd.DataFrame.set_index
porque el conjunto de claves es único para filas y columnas
df.set_index( ['key', 'row', 'item', 'col'] )['val0'].unstack(['item', 'col']).fillna(0).sort_index(1)
¿Puedo agregar la frecuencia en la que la columna y las filas aparecen juntas, también conocida como "tabulación cruzada"?
df.pivot_table(index='row', columns='col', fill_value=0, aggfunc='size') col col0 col1 col2 col3 col4 row row0 1 2 0 1 1 row2 1 0 2 1 2 row3 0 1 0 2 0 row4 0 1 2 2 1
df.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)
pd.crosstab(df['row'], df['col'])
# get integer factorization `i` and unique values `r` # for column `'row'` i, r = pd.factorize(df['row'].values) # get integer factorization `j` and unique values `c` # for column `'col'` j, c = pd.factorize(df['col'].values) # `n` will be the number of rows # `m` will be the number of columns n, m = r.size, c.size # `i * m + j` is a clever way of counting the # factorization bins assuming a flat array of length # `n * m`. Which is why we subsequently reshape as `(n, m)` b = np.bincount(i * m + j, minlength=n * m).reshape(n, m) # BTW, whenever I read this, I think 'Bean, Rice, and Cheese' pd.DataFrame(b, r, c) col3 col2 col0 col1 col4 row3 2 0 0 1 0 row2 1 2 1 0 2 row0 1 0 1 2 1 row4 2 2 0 1 1
pd.get_dummies(df['row']).T.dot(pd.get_dummies(df['col'])) col0 col1 col2 col3 col4 row0 1 2 0 1 1 row2 1 0 2 1 2 row3 0 1 0 2 0 row4 0 1 2 2 1
¿Cómo convierto un DataFrame de largo a ancho girando SOLO en dos columnas?
El primer paso es asignar un número a cada fila; este número será el índice de fila de ese valor en el resultado pivotado. Esto se hace usando GroupBy.cumcount
:
df2.insert(0, 'count', df2.groupby('A').cumcount()) df2 count AB 0 0 a 0 1 1 a 11 2 2 a 2 3 3 a 11 4 0 b 10 5 1 b 10 6 2 b 14 7 0 c 7
El segundo paso es usar la columna recién creada como índice para llamar a DataFrame.pivot
.
df2.pivot(*df2) # df2.pivot(index='count', columns='A', values='B') A abc count 0 0.0 10.0 7.0 1 11.0 10.0 NaN 2 2.0 14.0 NaN 3 11.0 NaN NaN
Mientras que DataFrame.pivot
solo acepta columnas, DataFrame.pivot_table
también acepta matrices, por lo que GroupBy.cumcount
se puede pasar directamente como index
sin crear una columna explícita.
df2.pivot_table(index=df2.groupby('A').cumcount(), columns='A', values='B') A abc 0 0.0 10.0 7.0 1 11.0 10.0 NaN 2 2.0 14.0 NaN 3 11.0 NaN NaN
¿Cómo aplano el índice múltiple a un solo índice después del
pivot
?
Si las columns
escriben object
con join
de cadenas
df.columns = df.columns.map('|'.join)
otro format
df.columns = df.columns.map('{0[0]}|{0[1]}'.format)