Como usuario de ambos Ry python, he visto este tipo de preguntas un par de veces.
En R, tienen la función incorporada del paquete tidyrllamado unnest. Pero en Python( pandas) no hay una función incorporada para este tipo de pregunta.
Sé que las objectcolumnas typesiempre hacen que los datos sean difíciles de convertir con una pandasfunción '. Cuando recibí los datos de esta manera, lo primero que me vino a la mente fue 'aplanar' o desanidar las columnas.
Estoy usando pandasy pythonfunciones para este tipo de pregunta. Si le preocupa la velocidad de las soluciones anteriores, verifique la respuesta del usuario3483203, ya que está usando numpyy la mayoría de las veces numpyes más rápido. Lo recomiendo Cpythony numbasi la velocidad importa en tu caso.
Método 0 [pandas> = 0.25]
A partir de pandas 0.25 , si solo necesita expandir una columna, puede usar la explodefunción:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Método 1
apply + pd.Series (fácil de entender, pero no se recomienda en términos de rendimiento).
df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
Out[463]:
A B
0 1 1
1 1 2
0 2 1
1 2 2
Método 2
Usando repeatcon el DataFrameconstructor, vuelva a crear su marco de datos (bueno en rendimiento, no bueno en múltiples columnas)
df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
df
Out[465]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
Método 2.1,
por ejemplo, además de A, tenemos A.1 ..... An Si todavía usamos el método ( Método 2 ) anterior, es difícil para nosotros volver a crear las columnas una por una.
Solución: joino mergecon el indexdespués de 'desanidar' las columnas individuales
s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
s.join(df.drop('B',1),how='left')
Out[477]:
B A
0 1 1
0 2 1
1 1 2
1 2 2
Si necesita el orden de las columnas exactamente igual que antes, agréguelo reindexal final.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Método 3
recrear ellist
pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
Out[488]:
A B
0 1 1
1 1 2
2 2 1
3 2 2
Si hay más de dos columnas, utilice
s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
s.merge(df,left_on=0,right_index=True)
Out[491]:
0 1 A B
0 0 1 1 [1, 2]
1 0 2 1 [1, 2]
2 1 1 2 [1, 2]
3 1 2 2 [1, 2]
Método 4
usando reindex oloc
df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
Out[554]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
#df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
Método 5
cuando la lista solo contiene valores únicos:
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
from collections import ChainMap
d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
pd.DataFrame(list(d.items()),columns=df.columns[::-1])
Out[574]:
B A
0 1 1
1 2 1
2 3 2
3 4 2
Método 6 que
utiliza numpypara un alto rendimiento:
newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
pd.DataFrame(data=newvalues[0],columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Método 7
usando la función base itertools cycley chain: solución de Python pura solo por diversión
from itertools import cycle,chain
l=df.values.tolist()
l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
Generalizando a múltiples columnas
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
df
Out[592]:
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4]
Función de autodefinición:
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
unnesting(df,['B','C'])
Out[609]:
B C A
0 1 1 1
0 2 2 1
1 3 3 2
1 4 4 2
Desanidación por columnas
Todo el método anterior está hablando de la eliminación vertical y la explosión.Si necesita gastar la lista horizontal , consulte con el pd.DataFrameconstructor.
df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
Out[33]:
A B C B_0 B_1
0 1 [1, 2] [1, 2] 1 2
1 2 [3, 4] [3, 4] 3 4
Función actualizada
def unnesting(df, explode, axis):
if axis==1:
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
Salida de prueba
unnesting(df, ['B','C'], axis=0)
Out[36]:
B0 B1 C0 C1 A
0 1 2 1 2 1
1 3 4 3 4 2