Después de mirar en diversos repositorios y foros de internet al final hemos hallado la solución que te compartimos más adelante.
Solución:
Tu puedes primero split
columnas, crear Series
por stack
y elimine los espacios en blanco strip
:
s1 = df.value.str.split(',', expand=True).stack().str.strip().reset_index(level=1, drop=True)
s2 = df.date.str.split(',', expand=True).stack().str.strip().reset_index(level=1, drop=True)
Entonces concat
ambos Series
a df1
:
df1 = pd.concat([s1,s2], axis=1, keys=['value','date'])
Quitar columnas antiguas value
y date
y join
:
print (df.drop(['value','date'], axis=1).join(df1).reset_index(drop=True))
ticker account value date
0 aa assets 100 20121231
1 aa assets 200 20131231
2 bb liabilities 50 20141231
3 bb liabilities 150 20131231
Noto mucho esta pregunta. Es decir, ¿cómo divido esta columna que tiene una lista en varias filas? Lo he visto llamado explotar. A continuación se muestran algunos enlaces:
- https://stackoverflow.com/a/38432346/2336654
- https://stackoverflow.com/a/38499036/2336654
Entonces escribí una función que lo hará.
def explode(df, columns):
idx = np.repeat(df.index, df[columns[0]].str.len())
a = df.T.reindex_axis(columns).values
concat = np.concatenate([np.concatenate(a[i]) for i in range(a.shape[0])])
p = pd.DataFrame(concat.reshape(a.shape[0], -1).T, idx, columns)
return pd.concat([df.drop(columns, axis=1), p], axis=1).reset_index(drop=True)
Pero antes de que podamos usarlo, necesitamos listas (o iterables) en una columna.
Configuración
df = pd.DataFrame([['aa', 'assets', '100,200', '20121231,20131231'],
['bb', 'liabilities', '50,50', '20141231,20131231']],
columns=['ticker', 'account', 'value', 'date'])
df
separar value
y date
columnas:
df.value = df.value.str.split(',')
df.date = df.date.str.split(',')
df
Ahora podríamos explotar en cualquiera de las columnas o en ambas, una tras otra.
Solución
explode(df, ['value','date'])
Momento
quite strip
del tiempo de @ jezrael porque no pude agregarlo efectivamente al mío. Este es un paso necesario para esta pregunta ya que OP tiene espacios en cadenas después de las comas. Mi objetivo era proporcionar una forma genérica de explotar una columna dado que ya tiene iterables y creo que lo he logrado.
código
def get_df(n=1):
return pd.DataFrame([['aa', 'assets', '100,200,200', '20121231,20131231,20131231'],
['bb', 'liabilities', '50,50', '20141231,20131231']] * n,
columns=['ticker', 'account', 'value', 'date'])
pequeña muestra de 2 filas
muestra mediana de 200 filas
muestra grande de 2.000.000 filas
escribí explode
función basada en respuestas anteriores. Puede ser útil para cualquiera que quiera agarrarlo y usarlo rápidamente.
def explode(df, cols, split_on=','):
"""
Explode dataframe on the given column, split on given delimeter
"""
cols_sep = list(set(df.columns) - set(cols))
df_cols = df[cols_sep]
explode_len = df[cols[0]].str.split(split_on).map(len)
repeat_list = []
for r, e in zip(df_cols.as_matrix(), explode_len):
repeat_list.extend([list(r)]*e)
df_repeat = pd.DataFrame(repeat_list, columns=cols_sep)
df_explode = pd.concat([df[col].str.split(split_on, expand=True).stack().str.strip().reset_index(drop=True)
for col in cols], axis=1)
df_explode.columns = cols
return pd.concat((df_repeat, df_explode), axis=1)
ejemplo dado de @piRSquared:
df = pd.DataFrame([['aa', 'assets', '100,200', '20121231,20131231'],
['bb', 'liabilities', '50,50', '20141231,20131231']],
columns=['ticker', 'account', 'value', 'date'])
explode(df, ['value', 'date'])
producción
+-----------+------+-----+--------+
| account|ticker|value| date|
+-----------+------+-----+--------+
| assets| aa| 100|20121231|
| assets| aa| 200|20131231|
|liabilities| bb| 50|20141231|
|liabilities| bb| 50|20131231|
+-----------+------+-----+--------+
Si eres capaz, eres capaz de dejar un escrito acerca de qué le añadirías a esta división.