问题描述
我有一个这样的数据框-
df = pd.DataFrame(
{'key': [1,2,3,4],'col1': [['apple','orange'],['pineapple'],['','','guava',''],'orange','apple','']],'col2': [['087','799'],['681'],['078'],['816','018']]
}
)
我需要拆分列'col1'和'col2'并创建单独的行,但是根据其索引映射列表元素。所需的输出是这个-
desired_df = pd.DataFrame(
{'key': [1,1,4,'col1': [['apple'],['orange'],['guava'],['apple']],'col2': [['087'],['799'],['816'],['018']]
}
)
在col1中,元素可能为空,但非空col1元素的总长度将与col2的相应元素的长度匹配。例如:df的第2行和第3行。
我尝试了以下操作,但没有成功-
df.set_index(['key'])[['col1','col2']].apply(pd.Series).stack().reset_index(level=1,drop=True)
请帮助。我还在学习Python。
解决方法
由于您知道每个列表中的非空元素的数量总是匹配的,因此您可以分别explode
每一列,过滤出空白,然后将结果重新加入。如果您想将.reset_index()
作为一列添加到'key'
上。
import pandas as pd
pd.concat([df.set_index('key')[[col]].explode(col).query(f'{col} != ""')
for col in ['col1','col2']],axis=1)
# Without the f-string
#pd.concat([df.set_index('key')[[col]].explode(col).query(col + ' != ""')
# for col in ['col1',axis=1)
col1 col2
key
1 apple 087
1 orange 799
2 pineapple 681
3 guava 078
4 orange 816
4 apple 018
如果您使用的pandas
的旧版本不允许使用explode
方法,请使用@BEN_YO's method to unnest。我将在此处复制相关代码,因为有几个不同的版本可供选择。
import numpy as np
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')
pd.concat([unnesting(df.set_index('key')[[col]],explode=[col]).query(f'{col} !=""')
for col in ['col1',axis=1)
# Same output as above
,
尝试像这样在旧的df上创建新的df
df['key'] = df.apply(lambda x: [x['key']]*len(x['col2']),axis=1)
lst_col = ['key','col1','col2']
df = pd.DataFrame({
col:[x for lst in list(df[col]) for x in lst if x!=""] for col in lst_col
})
输出
key col1 col2
0 1 apple 087
1 1 orange 799
2 2 pineapple 681
3 3 guava 078
4 4 orange 816
5 4 apple 018
,
出于复杂性考虑:)
pd.DataFrame([j for i in [[{"key": x['key'],"col1": y,'col2':x['col2'][list(filter(None,x['col1'])).index(y)]} for y in list(filter(None,x['col1']))]for idx,x in df.iterrows()] for j in i])
输出
| key | col1 | col2 |
|------:|:----------|-------:|
| 1 | apple | 087 |
| 1 | orange | 799 |
| 2 | pineapple | 681 |
| 3 | guava | 078 |
| 4 | orange | 816 |
| 4 | apple | 018 |
,
尝试
newkeys= list(itertools.chain.from_iterable(df.apply(lambda vals : [vals[0]]*len(vals[2]),axis=1).tolist()))
newcol1,newcol2 = list(itertools.chain.from_iterable(df.col1)),list(itertools.chain.from_iterable(df.col2))
newcol1=list(filter(None,newcol1))
pd.DataFrame(zip(*[newkeys,newcol1,newcol2]),columns=df.columns)