lla*_*fin 93 python dictionary dataframe pandas
我将数据保存在postgreSQL数据库中.我使用Python2.7查询这些数据并将其转换为Pandas DataFrame.但是,此数据框的最后一列中包含值的字典(或列表?).DataFrame看起来像这样:
[1] df
Station ID Pollutants
8809 {"a": "46", "b": "3", "c": "12"}
8810 {"a": "36", "b": "5", "c": "8"}
8811 {"b": "2", "c": "7"}
8812 {"c": "11"}
8813 {"a": "82", "c": "15"}
Run Code Online (Sandbox Code Playgroud)
我需要将此列拆分为单独的列,以便DataFrame如下所示:
[2] df2
Station ID a b c
8809 46 3 12
8810 36 5 8
8811 NaN 2 7
8812 NaN NaN 11
8813 82 NaN 15
Run Code Online (Sandbox Code Playgroud)
我遇到的主要问题是列表的长度不同.但是所有列表只包含相同的3个值:a,b和c.它们总是以相同的顺序出现(第一个,第二个,第三个).
以下代码用于工作并返回我想要的内容(df2).
[3] df
[4] objs = [df, pandas.DataFrame(df['Pollutant Levels'].tolist()).iloc[:, :3]]
[5] df2 = pandas.concat(objs, axis=1).drop('Pollutant Levels', axis=1)
[6] print(df2)
Run Code Online (Sandbox Code Playgroud)
我上周刚刚运行此代码并且工作正常.但现在我的代码坏了,我从第[4]行得到了这个错误:
IndexError: out-of-bounds on slice (end)
Run Code Online (Sandbox Code Playgroud)
我没有对代码进行任何更改,但现在收到了错误.我觉得这是因为我的方法不健全或不合适.
有关如何将此列列表拆分为单独列的任何建议或指导将非常感激!
编辑:我认为.tolist()和.apply方法不能处理我的代码,因为它是一个unicode字符串,即:
#My data format
u{'a': '1', 'b': '2', 'c': '3'}
#and not
{u'a': '1', u'b': '2', u'c': '3'}
Run Code Online (Sandbox Code Playgroud)
数据以此格式从postgreSQL数据库导入.有关此问题的任何帮助或想法?有没有办法转换unicode?
jor*_*ris 117
要将字符串转换为实际的字典,您可以这样做df['Pollutant Levels'].map(eval).之后,下面的解决方案可用于将字典转换为不同的列.
使用一个小例子,您可以使用.apply(pd.Series):
In [2]: df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]})
In [3]: df
Out[3]:
a b
0 1 {u'c': 1}
1 2 {u'd': 3}
2 3 {u'c': 5, u'd': 6}
In [4]: df['b'].apply(pd.Series)
Out[4]:
c d
0 1.0 NaN
1 NaN 3.0
2 5.0 6.0
Run Code Online (Sandbox Code Playgroud)
要将其与数据帧的其余部分组合,您可以concat使用以上结果的其他列:
In [7]: pd.concat([df.drop(['b'], axis=1), df['b'].apply(pd.Series)], axis=1)
Out[7]:
a c d
0 1 1.0 NaN
1 2 NaN 3.0
2 3 5.0 6.0
Run Code Online (Sandbox Code Playgroud)
使用您的代码,如果我遗漏了iloc部分,这也有用:
In [15]: pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1)
Out[15]:
a c d
0 1 1.0 NaN
1 2 NaN 3.0
2 3 5.0 6.0
Run Code Online (Sandbox Code Playgroud)
Tre*_*ney 36
dicts执行的时序分析,标准化一列平面的最快方法:
df.join(pd.DataFrame(df.pop('Pollutants').values.tolist()))list或列的其他问题dicts,例如带有NaN或嵌套 的行dicts。pd.json_normalize(df.Pollutants) 明显快于 df.Pollutants.apply(pd.Series)
%%timeit下文。对于 1M 行,.json_normalize比 快 47 倍.apply。dict列是否具有dict或str类型。
str类型,则必须将它们转换回dict类型,使用ast.literal_eval或json.loads(…)。pd.json_normalize转换的dicts,用keys的标题和values用于行。
record_path& meta)用于处理嵌套dicts.pandas.DataFrame.join原来的数据帧相结合,df与使用的列上创建pd.json_normalize
df.reset_index()获取整数索引,然后再进行规范化和连接。pandas.DataFrame.drop, 删除不需要的列dictsNaN,则必须用空填充dict
df.Pollutants = df.Pollutants.fillna({i: {} for i in df.index})
'Pollutants'列是字符串,请使用'{}'.import pandas as pd
from ast import literal_eval
import numpy as np
data = {'Station ID': [8809, 8810, 8811, 8812, 8813, 8814],
'Pollutants': ['{"a": "46", "b": "3", "c": "12"}', '{"a": "36", "b": "5", "c": "8"}', '{"b": "2", "c": "7"}', '{"c": "11"}', '{"a": "82", "c": "15"}', np.nan]}
df = pd.DataFrame(data)
# display(df)
Station ID Pollutants
0 8809 {"a": "46", "b": "3", "c": "12"}
1 8810 {"a": "36", "b": "5", "c": "8"}
2 8811 {"b": "2", "c": "7"}
3 8812 {"c": "11"}
4 8813 {"a": "82", "c": "15"}
5 8814 NaN
# replace NaN with '{}' if the column is strings, otherwise replace with {}
# df.Pollutants = df.Pollutants.fillna('{}') # if the NaN is in a column of strings
df.Pollutants = df.Pollutants.fillna({i: {} for i in df.index}) # if the column is not strings
# Convert the column of stringified dicts to dicts
# skip this line, if the column contains dicts
df.Pollutants = df.Pollutants.apply(literal_eval)
# reset the index if the index is not unique integers from 0 to n-1
# df.reset_index(inplace=True) # uncomment if needed
# normalize the column of dictionaries and join it to df
df = df.join(pd.json_normalize(df.Pollutants))
# drop Pollutants
df.drop(columns=['Pollutants'], inplace=True)
# display(df)
Station ID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
5 8814 NaN NaN NaN
Run Code Online (Sandbox Code Playgroud)
%%timeit# dataframe with 1M rows
dfb = pd.concat([df]*200000).reset_index(drop=True)
%%timeit
dfb.join(pd.json_normalize(dfb.Pollutants))
[out]:
5.44 s ± 32.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
pd.concat([dfb.drop(columns=['Pollutants']), dfb.Pollutants.apply(pd.Series)], axis=1)
[out]:
4min 17s ± 2.44 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
Run Code Online (Sandbox Code Playgroud)
小智 26
我知道这个问题已经很老了,但是我到这里来寻找答案。实际上,现在有一种更好(更快)的方法json_normalize:
import pandas as pd
from pandas.io.json import json_normalize
df2 = json_normalize(df['Pollutant Levels'])
Run Code Online (Sandbox Code Playgroud)
这避免了昂贵的应用功能...
Mer*_*lin 17
试试这个: 从SQL返回的数据必须转换成Dict.
或者"Pollutant Levels" 现在可能 是Pollutants'
StationID Pollutants
0 8809 {"a":"46","b":"3","c":"12"}
1 8810 {"a":"36","b":"5","c":"8"}
2 8811 {"b":"2","c":"7"}
3 8812 {"c":"11"}
4 8813 {"a":"82","c":"15"}
df2["Pollutants"] = df2["Pollutants"].apply(lambda x : dict(eval(x)) )
df3 = df2["Pollutants"].apply(pd.Series )
a b c
0 46 3 12
1 36 5 8
2 NaN 2 7
3 NaN NaN 11
4 82 NaN 15
result = pd.concat([df, df3], axis=1).drop('Pollutants', axis=1)
result
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
Run Code Online (Sandbox Code Playgroud)
Haf*_*man 11
Merlin的答案更好,更简单,但我们不需要lambda函数。可以通过以下两种方式之一安全地忽略对字典的评估:
方法1:两步
# step 1: convert the `Pollutants` column to Pandas dataframe series
df_pol_ps = data_df['Pollutants'].apply(pd.Series)
df_pol_ps:
a b c
0 46 3 12
1 36 5 8
2 NaN 2 7
3 NaN NaN 11
4 82 NaN 15
# step 2: concat columns `a, b, c` and drop/remove the `Pollutants`
df_final = pd.concat([df, df_pol_ps], axis = 1).drop('Pollutants', axis = 1)
df_final:
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
Run Code Online (Sandbox Code Playgroud)
方式2:以上两个步骤可以一次性组合:
df_final = pd.concat([df, df['Pollutants'].apply(pd.Series)], axis = 1).drop('Pollutants', axis = 1)
df_final:
StationID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
Run Code Online (Sandbox Code Playgroud)
你可以用join与pop+ tolist.性能concat与drop+ 相当tolist,但有些人可能会发现这种语法更清晰:
res = df.join(pd.DataFrame(df.pop('b').tolist()))
Run Code Online (Sandbox Code Playgroud)
使用其他方法进行基准测试:
df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]})
def joris1(df):
return pd.concat([df.drop('b', axis=1), df['b'].apply(pd.Series)], axis=1)
def joris2(df):
return pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1)
def jpp(df):
return df.join(pd.DataFrame(df.pop('b').tolist()))
df = pd.concat([df]*1000, ignore_index=True)
%timeit joris1(df.copy()) # 1.33 s per loop
%timeit joris2(df.copy()) # 7.42 ms per loop
%timeit jpp(df.copy()) # 7.68 ms per loop
Run Code Online (Sandbox Code Playgroud)
如何使用 Pandas 将一列字典拆分为单独的列?
pd.DataFrame(df['val'].tolist()) 是爆炸一列字典的规范方法这是您使用彩色图表的证明。
基准代码供参考。
请注意,我只是在计算爆炸的时间,因为这是回答这个问题最有趣的部分 - 结果构造的其他方面(例如是否使用pop或drop)与讨论无关,可以忽略(但应注意,使用pop避免后续drop调用,因此最终解决方案的性能更高一些,但我们仍在列出该列并将其传递给pd.DataFrame任何一种方式)。
此外,pop破坏性地改变输入数据帧,使得在假设输入在测试运行中没有改变的基准代码中运行变得更加困难。
df['val'].apply(pd.Series)对于大 N 来说非常慢,因为 Pandas 为每一行构造 Series 对象,然后继续从它们构造一个 DataFrame。对于较大的 N,性能下降到几分钟或几小时的数量级。
pd.json_normalize(df['val']))速度较慢,因为json_normalize它旨在处理更复杂的输入数据 - 特别是具有多个记录路径和元数据的深度嵌套 JSON。我们有一个简单的扁平字典pd.DataFrame就足够了,所以如果你的字典是扁平的,就使用它。
一些答案建议df.pop('val').values.tolist()或df.pop('val').to_numpy().tolist()。我认为无论是列出系列还是 numpy 数组都没有太大区别。直接列出系列是少一个操作,而且速度确实不慢,所以我建议避免在中间步骤中生成 numpy 数组。
我强烈建议该方法提取“污染物”列:
df_pollutants = pd.DataFrame(df['Pollutants'].values.tolist(), index=df.index)
它比
df_pollutants = df['Pollutants'].apply(pd.Series)
当df的大小很大时。
一行解决方案如下:
>>> df = pd.concat([df['Station ID'], df['Pollutants'].apply(pd.Series)], axis=1)
>>> print(df)
Station ID a b c
0 8809 46 3 12
1 8810 36 5 8
2 8811 NaN 2 7
3 8812 NaN NaN 11
4 8813 82 NaN 15
Run Code Online (Sandbox Code Playgroud)
>>> df
Station ID Pollutants
0 8809 {"a": "46", "b": "3", "c": "12"}
1 8810 {"a": "36", "b": "5", "c": "8"}
2 8811 {"b": "2", "c": "7"}
3 8812 {"c": "11"}
4 8813 {"a": "82", "c": "15"}
Run Code Online (Sandbox Code Playgroud)
1000 万行大型数据集的速度比较
>>> df = pd.concat([df]*100000).reset_index(drop=True)
>>> df = pd.concat([df]*20).reset_index(drop=True)
>>> print(df.shape)
(10000000, 2)
Run Code Online (Sandbox Code Playgroud)
def apply_drop(df):
return df.join(df['Pollutants'].apply(pd.Series)).drop('Pollutants', axis=1)
def json_normalise_drop(df):
return df.join(pd.json_normalize(df.Pollutants)).drop('Pollutants', axis=1)
def tolist_drop(df):
return df.join(pd.DataFrame(df['Pollutants'].tolist())).drop('Pollutants', axis=1)
def vlues_tolist_drop(df):
return df.join(pd.DataFrame(df['Pollutants'].values.tolist())).drop('Pollutants', axis=1)
def pop_tolist(df):
return df.join(pd.DataFrame(df.pop('Pollutants').tolist()))
def pop_values_tolist(df):
return df.join(pd.DataFrame(df.pop('Pollutants').values.tolist()))
Run Code Online (Sandbox Code Playgroud)
>>> %timeit apply_drop(df.copy())
1 loop, best of 3: 53min 20s per loop
>>> %timeit json_normalise_drop(df.copy())
1 loop, best of 3: 54.9 s per loop
>>> %timeit tolist_drop(df.copy())
1 loop, best of 3: 6.62 s per loop
>>> %timeit vlues_tolist_drop(df.copy())
1 loop, best of 3: 6.63 s per loop
>>> %timeit pop_tolist(df.copy())
1 loop, best of 3: 5.99 s per loop
>>> %timeit pop_values_tolist(df.copy())
1 loop, best of 3: 5.94 s per loop
Run Code Online (Sandbox Code Playgroud)
+---------------------+-----------+
| apply_drop | 53min 20s |
| json_normalise_drop | 54.9 s |
| tolist_drop | 6.62 s |
| vlues_tolist_drop | 6.63 s |
| pop_tolist | 5.99 s |
| pop_values_tolist | 5.94 s |
+---------------------+-----------+
Run Code Online (Sandbox Code Playgroud)
df.join(pd.DataFrame(df.pop('Pollutants').values.tolist()))是最快的
| 归档时间: |
|
| 查看次数: |
53682 次 |
| 最近记录: |