Pandas: разбить строку на две или более строк при применении построчной функции - PullRequest
3 голосов
/ 15 марта 2020

У меня есть кадр данных в pandas, который выглядит следующим образом:

df = pd.DataFrame([[4, 9],[4,9],[[1,2],[3,4]]], columns=['A', 'B'])
df

    A   B
0   4   9
1   4   9
2   [1, 2]  [3, 4]

Однако я хотел бы преобразовать его в таблицу, подобную этой:

    A   B
0   4   9
1   4   9
2   1   2
3   3   4

Есть ли способ применить мудрую функцию строки (используя df.apply(function,axis=1,...) или другую функцию в pandas), чтобы сделать это?

Ответы [ 7 ]

3 голосов
/ 15 марта 2020

Использование lis-понимания с сглаживанием значений на chain:

from  itertools import chain

out = list(chain.from_iterable(item if isinstance(item[0],list) 
             else [item] for item in df[['A','B']].values))
df1 = pd.DataFrame(out, columns=['A','B'])

Или l oop, альтернатива:

out = []
for x in df[['A','B']].values:
    if isinstance(x[0], list):
        for y in x:
            out.append(y)
    else:
        out.append(x)

df1 = pd.DataFrame(out, columns=['A','B'])
print (df1)
   A  B
0  4  9
1  4  9
2  1  2
3  3  4
1 голос
/ 15 марта 2020

Вы можете сделать:

#main piece - the rest is actually 'fixing' the multiindex piece to fit your purpose:
df=df.stack().explode().to_frame()

df["id"]=df.groupby(level=[0,1]).cumcount()

df.index=pd.MultiIndex.from_tuples(zip(df.index.get_level_values(0)+df['id'], df.index.get_level_values(1)))

df=df.drop(columns="id").unstack()

df.columns=map(lambda x: x[1], df.columns)

Выходы:

>>> df

   A  B
0  4  9
1  4  9
2  1  3
3  2  4
1 голос
/ 15 марта 2020

Использование понимания списка с concat:

df = pd.DataFrame([[4, 9],[4,9],[[1,2],[3,4]],], columns=['A', 'B'])

print (pd.concat([df.loc[:1], *[pd.DataFrame(list(i),columns=df.columns) for i in df.loc[2:].to_numpy()]],
                 ignore_index=True))
   A  B
0  4  9
1  4  9
2  1  2
3  3  4
0 голосов
/ 15 марта 2020

Есть одна проблема в вопросе, это не уверен, что элементы списка в одной строке всегда имеют одинаковую длину. Если это предположение выполнено, то для него работает следующий ответ:

df.apply(pd.Series.explode) 


    A   B
0   4   9
1   4   9
2   1   3
2   2   4

0 голосов
/ 15 марта 2020

Еще одно возможное решение для всех других, предложенных на данный момент, с использованием DataFrame.melt , DataFrame.explode и DataFrame.pivot :

import pandas as pd

df = pd.DataFrame([[4, 9],[4,9],[[1,2],[3,4]]], columns=['A', 'B'])
# Create index column
df.reset_index(inplace=True)

tmp = df.melt(id_vars='index', var_name='columns').explode('value')

# Define indexes
idx = sum([list(range(len(tmp)//tmp['columns'].nunique())) for _ in range(tmp['columns'].nunique())], [])
tmp['index'] = idx

result_df = tmp.pivot(index='index', columns='columns', values='value')

result_df
columns  A  B
index        
0        4  9
1        4  9
2        1  3
3        2  4
0 голосов
/ 15 марта 2020

Использование простых циклов for и if:

 alist = df['A'].tolist()
 blist = df['B'].tolist()

 alist1=[]
 blist1=[]
 for k,r in zip(alist,blist):
   if isinstance(k,list):
     alist1.append(k[0])
     blist1.append(k[1])
   if isinstance(r,list):
     alist1.append(r[0])
     blist1.append(r[1])
   else:
     alist1.append(k)
     blist1.append(r)

df = pd.DataFrame({'A': alist1, 'b': blist1})
0 голосов
/ 15 марта 2020

Использование DataFrame.apply, Series.explode, DataFrame.mask и DataFrame.where:

types = df.applymap(type).eq(list)
arr = df.where(types).apply(pd.Series.explode).dropna().T.to_numpy()
df.mask(types).dropna().append(pd.DataFrame(arr, columns=df.columns), ignore_index=True)

   A  B
0  4  9
1  4  9
2  1  2
3  3  4
...