如何展开数据框中包含不同长度字典的单列并保留原有列信息
如何展开数据框中包含不同长度字典的单列并保留原有列信息
嘿,我完全懂你的需求——你想把那个装着多层字典的sample列拆成单独的列(比如a和b),同时还要牢牢保留原数据里的age、date这些关键信息,每个子字典对应一行,重复原有列的内容对吧?你的尝试方向是对的,只是可以调整一下实现方式,让结果完全贴合你的预期。
先拿你给出的示例数据来演示,首先我们先把数据构造出来:
import pandas as pd data = { 'age': [59, 18, 63], 'sample': [ {'0': {'a':'yes', 'b': 'no'}, '1':{'a': 'maybe', 'b': 'NA'}}, {'0': {'a':'no', 'b': 'yes'}}, {'0': {'a':'no', 'b': 'no'}, '1':{'a': 'no', 'b': 'yes'}, '2':{'a': 'yes', 'b': 'yes'}} ], 'date': ['2022-09-29', '2022-08-29', '2022-07-29'] } df = pd.DataFrame(data)
方法一:高效的向量化操作(适合大数据量)
如果你的数据量很大,推荐用这种方法,因为explode和json_normalize都是pandas的高效向量化操作,比逐行遍历快得多:
# 第一步:把每个sample字典里的子字典提取成列表(去掉外层的0、1、2这些键) df['sample_list'] = df['sample'].apply(lambda x: list(x.values())) # 第二步:展开这个列表列,让每个子字典单独占一行 df_exploded = df.explode('sample_list').reset_index(drop=True) # 第三步:把sample_list里的子字典拆成单独的列,和原有列合并 df_final = pd.concat( [df_exploded.drop(['sample', 'sample_list'], axis=1), pd.json_normalize(df_exploded['sample_list'])], axis=1 )
执行完之后,df_final就是你想要的结果:原有age、date列完整保留,a和b成为独立列,每个子字典对应一行,重复对应物种(这里是age/date)的信息。
方法二:直观的逐行处理(适合小数据量)
如果数据量不大,这种方法逻辑更直观,容易理解:
def expand_row(row): # 把当前行的sample字典转成DataFrame sample_df = pd.DataFrame.from_dict(row['sample'], orient='index') # 把当前行的age、date添加到这个DataFrame里,重复对应行数 sample_df['age'] = row['age'] sample_df['date'] = row['date'] return sample_df # 对每一行应用函数,然后拼接所有结果 result = pd.concat([expand_row(row) for _, row in df.iterrows()], ignore_index=True) # 调整列顺序(可选,让原有列在前) result = result[['age', 'date', 'a', 'b']]
为什么你之前的尝试没完全符合预期?
你之前用pd.concat(t['sample'].apply(pd.DataFrame).tolist(), keys=t["age"]).reset_index(level="age")的方法,只把age列加回去了,没带上date,而且通过上面的方法,我们可以更灵活地把所有原有列都保留下来,同时直接让a、b成为普通列,而不是处理索引的问题。
最终得到的结果会是这样的:
| age | date | a | b |
|---|---|---|---|
| 59 | 2022-09-29 | yes | no |
| 59 | 2022-09-29 | maybe | NA |
| 18 | 2022-08-29 | no | yes |
| 63 | 2022-07-29 | no | no |
| 63 | 2022-07-29 | no | yes |
| 63 | 2022-07-29 | yes | yes |
备注:内容来源于stack exchange,提问作者redleesy
相关产品推荐
相关产品推荐

