Python 2.7 如何处理每个单元格中都有dicts列表的pandas列
我有一个数据帧,其中包括一列,其中每个单元格由一个dict列表组成,每个dict列表的长度都不同(包括0) 例如:Python 2.7 如何处理每个单元格中都有dicts列表的pandas列,python-2.7,pandas,pytables,hdfstore,Python 2.7,Pandas,Pytables,Hdfstore,我有一个数据帧,其中包括一列,其中每个单元格由一个dict列表组成,每个dict列表的长度都不同(包括0) 例如: df = pd.DataFrame({'ID' : [13423,294847,322844,429847], 'RANKS': [[{u'name': u'A', u'price': u'$1.00', u'rank': u'1'}, {u'name': u'B', u'price': u'$4.00', u'rank': u'2'}, {u'name': u'C', u'pri
df = pd.DataFrame({'ID' : [13423,294847,322844,429847], 'RANKS': [[{u'name': u'A', u'price': u'$1.00', u'rank': u'1'},
{u'name': u'B', u'price': u'$4.00', u'rank': u'2'},
{u'name': u'C', u'price': u'$3.99', u'rank': u'3'},
{u'name': u'D', u'price': u'$2.00', u'rank': u'4'},
{u'name': u'E', u'price': u'$2.50', u'rank': u'5'}],
[{u'name': u'AA', u'price': u'$1.99', u'rank': u'1'},
{u'name': u'BB', u'price': u'$6.99', u'rank': u'2'}],
[{u'name': u'Z', u'price': u'$0.99', u'rank': u'1'},
{u'name': u'Y', u'price': u'$10.00', u'rank': u'2'},
{u'name': u'X', u'price': u'$1.99', u'rank': u'3'}],[]], 'count' : [5,2,3,0]})
注意,“count”是“RANKS”中dict的数量。我的目标是创建一系列额外的数据帧/表(每个“rank”对应一个),并将它们链接到HDFStore中的主表。比如:
Rank_2
ID Price Name
13423 $4.00 B
294847 $6.99 BB
322844 $10.99 Y
429847 NaN NaN
Rank_3
ID Price Name
13423 $3.99 C
294847 NaN NaN
322844 $1.99 X
429847 NaN NaN
这样,如果需要的话,我可以轻松地查询ID和rank,但是主表不会因为层次数据的展开而变得混乱
然而,问题是,我不知道如何从这个列创建数据帧。我已经尝试了很多方法,第一种(如果有效,则嵌套在for循环中,但当然没有):
第二,因为价格对我来说是最重要的:
for i in range(0,5):
df['rank_%s' % str(i+1)] = df[df['count'] > i]['RANKS'].map(lambda x: x[i]['price'].strip('$'))
然后转换为float。这是可行的,但却是一个相当大的妥协。是否有一种有效的方法(不会挂断NAN)来实现我的目标,即为每个级别分别设置数据帧?我的直觉反应是,您可能不应该将数据帧拆分为 许多较小的数据帧。处理大量小数据帧需要Python 循环,通常是缓慢路径上的一步。相反,我认为你 最好使用一个数据帧,这样可以使dict列表变得平坦 每个内部dict在数据帧中都有自己的行。钥匙 内部目录将成为新的列。我怀疑这种单一的平面数据帧格式 能够做任何多数据帧替代方案可以做的事情,但 速度更快,而且保存到HDFStore也更简单 假设您有一个数据帧,在
列中有一个dict列表:
import numpy as np
import pandas as pd
df = pd.DataFrame({'ID' : [13423,294847,322844,429847], 'RANKS': [[{u'name': u'A', u'price': u'$1.00', u'rank': u'1'},
{u'name': u'B', u'price': u'$4.00', u'rank': u'2'},
{u'name': u'C', u'price': u'$3.99', u'rank': u'3'},
{u'name': u'D', u'price': u'$2.00', u'rank': u'4'},
{u'name': u'E', u'price': u'$2.50', u'rank': u'5'}],
[{u'name': u'AA', u'price': u'$1.99', u'rank': u'1'},
{u'name': u'BB', u'price': u'$6.99', u'rank': u'2'}],
[{u'name': u'Z', u'price': u'$0.99', u'rank': u'1'},
{u'name': u'Y', u'price': u'$10.00', u'rank': u'2'},
{u'name': u'X', u'price': u'$1.99', u'rank': u'3'}],[]], 'count' : [5,2,3,0]})
然后,您可以构建一个每行一个dict的平面数据帧,如下所示:
result = []
for idx, row in df.iterrows():
for dct in row['RANKS']:
dct['ID'] = row['ID']
dct['count'] = row['count']
result.append(dct)
del df
result = pd.DataFrame(result)
result['rank'] = result['rank'].astype(np.int32)
result['price'] = result['price'].str.replace('$', '')
result['price'] = result['price'].astype('float')
print(result)
产生
ID count name price rank
0 13423 5 A 1.00 1
1 13423 5 B 4.00 2
2 13423 5 C 3.99 3
3 13423 5 D 2.00 4
4 13423 5 E 2.50 5
5 294847 2 AA 1.99 1
6 294847 2 BB 6.99 2
7 322844 3 Z 0.99 1
8 322844 3 Y 10.00 2
9 322844 3 X 1.99 3
请注意,直接从原始数据源构建result
(从而完全避免df
)将是一个更干净、内存需求更少的解决方案。我刚刚遇到了一个类似的场景,下面是我最后如何解决它的:
In [1]: import pandas as pd
In [2]: df = pd.DataFrame({
...: 'ID' : [13423,294847,322844,429847],
...: 'RANKS': [[{u'name': u'A', u'price': u'$1.00', u'rank': u'1'},
...: {u'name': u'B', u'price': u'$4.00', u'rank': u'2'},
...: {u'name': u'C', u'price': u'$3.99', u'rank': u'3'},
...: {u'name': u'D', u'price': u'$2.00', u'rank': u'4'},
...: {u'name': u'E', u'price': u'$2.50', u'rank': u'5'}],
...:
...: [{u'name': u'AA', u'price': u'$1.99', u'rank': u'1'},
...: {u'name': u'BB', u'price': u'$6.99', u'rank': u'2'}],
...:
...: [{u'name': u'Z', u'price': u'$0.99', u'rank': u'1'},
...: {u'name': u'Y', u'price': u'$10.00', u'rank': u'2'},
...: {u'name': u'X', u'price': u'$1.99', u'rank': u'3'}],[]]})
In [3]: import itertools
In [4]: temp_df = pd.DataFrame(
...: list(itertools.chain(*[zip([key]*len(val), val)
...: for key, val in df.RANKS.iteritems()])),
...: columns=['idx', 'explode'])
In [5]: exploded = pd.merge(
...: df.drop('RANKS', axis=1),
...: temp_df.explode.apply(pd.Series).join(temp_df.idx),
...: left_index=True,
...: right_on='idx',
...: how='left').drop('idx', axis=1)
分解的数据框如下所示:
In [6]: exploded
Out[6]:
ID name price rank
0 13423 A $1.00 1
1 13423 B $4.00 2
2 13423 C $3.99 3
3 13423 D $2.00 4
4 13423 E $2.50 5
5 294847 AA $1.99 1
6 294847 BB $6.99 2
7 322844 Z $0.99 1
8 322844 Y $10.00 2
9 322844 X $1.99 3
9 429847 NaN NaN NaN
在Pandas版本0.25.0
中,有df.explode
方法用于列表爆炸,还有一些小代码用于dict爆炸
如果您的数据帧是:
import numpy as np
import pandas as pd
df = pd.DataFrame({'ID' : [13423,294847,322844,429847], 'RANKS': [[{u'name': u'A', u'price': u'$1.00', u'rank': u'1'},
{u'name': u'B', u'price': u'$4.00', u'rank': u'2'},
{u'name': u'C', u'price': u'$3.99', u'rank': u'3'},
{u'name': u'D', u'price': u'$2.00', u'rank': u'4'},
{u'name': u'E', u'price': u'$2.50', u'rank': u'5'}],
[{u'name': u'AA', u'price': u'$1.99', u'rank': u'1'},
{u'name': u'BB', u'price': u'$6.99', u'rank': u'2'}],
[{u'name': u'Z', u'price': u'$0.99', u'rank': u'1'},
{u'name': u'Y', u'price': u'$10.00', u'rank': u'2'},
{u'name': u'X', u'price': u'$1.99', u'rank': u'3'}],[]], 'count' : [5,2,3,0]})
然后,要分解列表,可以执行以下操作:
df = df.explode('RANKS')
df.reset_index(drop=True, inplace=True)
# Replace NaN by empty dict
def replace_nans_with_dict(series):
for idx in series[series.isnull()].index:
series.at[idx] = {}
return series
# Explodes list and dicts
def df_explosion(df, col_name:str):
if df[col_name].isna().any():
df[col_name] = replace_nans_with_dict(df[col_name])
df.reset_index(drop=True, inplace=True)
df1 = pd.DataFrame(df.loc[:,col_name].values.tolist())
df = pd.concat([df,df1], axis=1)
df.drop([col_name], axis=1, inplace=True)
return df
这给了你
ID RANKS count
0 13423 {'name': 'A', 'price': '$1.00', 'rank': '1'} 5
0 13423 {'name': 'B', 'price': '$4.00', 'rank': '2'} 5
0 13423 {'name': 'C', 'price': '$3.99', 'rank': '3'} 5
0 13423 {'name': 'D', 'price': '$2.00', 'rank': '4'} 5
0 13423 {'name': 'E', 'price': '$2.50', 'rank': '5'} 5
1 294847 {'name': 'AA', 'price': '$1.99', 'rank': '1'} 2
1 294847 {'name': 'BB', 'price': '$6.99', 'rank': '2'} 2
2 322844 {'name': 'Z', 'price': '$0.99', 'rank': '1'} 3
2 322844 {'name': 'Y', 'price': '$10.00', 'rank': '2'} 3
2 322844 {'name': 'X', 'price': '$1.99', 'rank': '3'} 3
3 429847 NaN 0
要分解这些DICT并将其展开为列,可以执行以下操作:
df = df.explode('RANKS')
df.reset_index(drop=True, inplace=True)
# Replace NaN by empty dict
def replace_nans_with_dict(series):
for idx in series[series.isnull()].index:
series.at[idx] = {}
return series
# Explodes list and dicts
def df_explosion(df, col_name:str):
if df[col_name].isna().any():
df[col_name] = replace_nans_with_dict(df[col_name])
df.reset_index(drop=True, inplace=True)
df1 = pd.DataFrame(df.loc[:,col_name].values.tolist())
df = pd.concat([df,df1], axis=1)
df.drop([col_name], axis=1, inplace=True)
return df
跑
你将有:
ID count name price rank
0 13423 5 A $1.00 1
1 13423 5 B $4.00 2
2 13423 5 C $3.99 3
3 13423 5 D $2.00 4
4 13423 5 E $2.50 5
5 294847 2 AA $1.99 1
6 294847 2 BB $6.99 2
7 322844 3 Z $0.99 1
8 322844 3 Y $10.00 2
9 322844 3 X $1.99 3
10 429847 0 NaN NaN NaN
杰姆斯,你应该考虑把你的帖子分成两个问题。一个用于将数据帧拆分为多个数据帧,另一个用于HDFStore问题。可以。谢谢你,鲍勃。我的直觉反应是,你可能不应该把你的数据帧分割成许多更小的数据帧。处理大量小数据帧需要Python循环,这通常是缓慢的一步。相反,我认为使用一个数据帧可能会更好,它将dict列表展平,这样每个内部dict在数据帧中都有自己的行。内部dict的键将成为新列。我怀疑这种单一的平面数据帧格式除了速度更快之外,还能做多个数据帧替代方案所能做的任何事情,并且它会使保存到HDFStore变得简单。链接到第二个问题(有些相关的帖子):@unutbu感谢您的回复。我肯定有点同意。而且您不认为按照您的建议将其扁平化会对已经很大的数据帧(使用了150万行和~9GB的ram)造成任何性能问题吗?非常感谢。我真的很感激你的建议。我有一个想法,我很快就会尝试:为了解决内存问题,我将创建一个小数据框,用于展开嵌套列(根据您的代码),然后将ID上的数据框与主数据框合并。这样,就没有重复的“df”,只是一个由ID和rank info.Update组成的附加数据框(有点晚了):这是我最后使用的策略。派对晚了五年,但这个答案最终解决了我整天都在研究的问题,非常感谢!