python - Convertir valores en columnas
pandas (5)
Disculpas por el vago nombre de la pregunta, pero no estoy realmente seguro de cómo llamar a esta operación.
Tengo el siguiente marco de datos:
import pandas as pd
df = pd.DataFrame({
''A'': [1, 3, 2, 1, 2],
''B'': [2, 1, 3, 2, 3],
''C'': [3, 2, 1, 3, 1],
})
print(df)
# A B C
# 0 1 2 3
# 1 3 1 2
# 2 2 3 1
# 3 1 2 3
# 4 2 3 1
Estos datos representan una "clasificación" de cada una de las opciones,
A
,
B
y
C
para cada fila.
Entonces, por ejemplo, en la fila
2
,
C
fue el mejor, luego
A
, luego
B
Me gustaría construir el marco de datos "invertido", donde, para cada fila, tengo tres columnas para la posición
1
,
2
y
3
de la clasificación, siendo
A
,
B
y
C
los datos.
Entonces, para el ejemplo anterior, el resultado sería:
out = pd.DataFrame({
1: [''A'', ''B'', ''C'', ''A'', ''C''],
2: [''B'', ''C'', ''A'', ''B'', ''A''],
3: [''C'', ''A'', ''B'', ''C'', ''B''],
})
print(out)
# 1 2 3
# 0 A B C
# 1 B C A
# 2 C A B
# 3 A B C
# 4 C A B
Idealmente, cada fila en
df
debería tener los tres valores distintos
1
,
2
y
3
, pero puede haber casos con valores repetidos (los valores fuera de ese rango no necesitan ser considerados).
Si es posible, me gustaría resolver esto "concatenando" los nombres de las opciones en la misma posición y teniendo cadenas vacías o NaN en las posiciones faltantes.
Por ejemplo, con esta entrada:
df_bad = pd.DataFrame({''A'': [1], ''B'': [2], ''C'': [2]})
print(df_bad)
# A B C
# 0 1 2 2
Idealmente, me gustaría obtener esta salida:
out_bad = pd.DataFrame({1: [''A''], 2: [''BC''], 3: ['''']})
print(out_bad)
# 1 2 3
# 0 A BC
Alternativamente, podría conformarme con obtener uno de los valores en lugar de la concatenación.
He estado buscando a través de
melt
,
pivot
,
pivot_table
y otras funciones, pero no puedo encontrar la manera de obtener el resultado que quiero.
De otra manera:
df = pd.DataFrame({
''A'': [1, 3, 2, 1, 2],
''B'': [2, 1, 3, 2, 3],
''C'': [3, 2, 1, 2, 1],
})
(df.stack()
.reset_index()
.groupby([''level_0'',0])
.level_1.apply(''''.join)
.unstack()
)
Salida:
0 1 2 3
level_0
0 A B C
1 B C A
2 C A B
3 A BC NaN
4 C A B
Puedes usar argsort :
pd.DataFrame(df.columns.values[np.argsort(df.values)])
0 1 2
0 A B C
1 B C A
2 C A B
3 A B C
4 C A B
Su primer ejemplo se puede resolver de manera eficiente con
argsort
e indexación.
m = np.argsort(df.to_numpy(), 1)
df.columns.to_numpy()[m]
array([[''A'', ''B'', ''C''],
[''B'', ''C'', ''A''],
[''C'', ''A'', ''B''],
[''A'', ''B'', ''C''],
[''C'', ''A'', ''B'']], dtype=object)
El segundo ejemplo es un poco complicado, pero sigue siendo la misma idea, actualizaré en breve.
aquí hay una stack ida
df.stack().reset_index(level=1).set_index(0,append=True)[''level_1''].unstack()
Out[89]:
0 1 2 3
0 A B C
1 B C A
2 C A B
3 A B C
4 C A B
para casos de clasificación duplicados, como el segundo ejemplo, cualquier solución que utilice
pivot
y
unstack
en el último paso fallará.
Necesita
pivot_table
o
crosstab
pivot_table
crosstab
.
Como ya descubriste la solución usando
pivot_table
.
Aquí está la
crosstab
df2 = df_bad.stack().reset_index(1, name=''cols'')
pd.crosstab(index=df2.index, columns=df2.cols, values=df2.level_1,
aggfunc=''''.join).fillna('''')
Out[171]:
cols 1 2
row_0
0 A BC
Usar
stack
y
pivot
df.stack().reset_index(1, name=''cols'').pivot(columns=''cols'', values=''level_1'')
Out[131]:
cols 1 2 3
0 A B C
1 B C A
2 C A B
3 A B C
4 C A B