问题:将“熊猫”列中的字典/列表拆分为单独的列
我将数据保存在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"}
我需要将此列拆分为单独的列,以便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
我遇到的主要问题是列表的长度不同。但是所有列表最多只能包含相同的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)
我上周才运行此代码,并且运行良好。但是现在我的代码坏了,我从第[4]行得到了这个错误:
IndexError: out-of-bounds on slice (end)
我没有对代码进行任何更改,但是现在出现了错误。我觉得这是由于我的方法不够健壮或不合适。
对于如何将列表的此列拆分为单独的列的任何建议或指导,将不胜感激!
编辑:我认为.tolist()和.apply方法不适用于我的代码,因为它是一个unicode字符串,即:
#My data format
u{'a': '1', 'b': '2', 'c': '3'}
#and not
{u'a': '1', u'b': '2', u'c': '3'}
数据是从PostgreSQL数据库以这种格式导入的。这个问题有什么帮助或想法吗?有没有办法转换unicode?
回答 0
要将字符串转换为实际的dict,可以执行df['Pollutant Levels'].map(eval)
。之后,可以使用以下解决方案将dict转换为不同的列。
通过一个小例子,您可以使用.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
要将其与数据框的其余部分合并,可以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
使用我的代码,如果我省略了这一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
回答 1
我知道这个问题已经很老了,但是我到这里来寻找答案。实际上,现在有一种更好(更快)的方法json_normalize
:
import pandas as pd
df2 = pd.json_normalize(df['Pollutant Levels'])
这避免了昂贵的应用功能…
回答 2
尝试以下操作: 从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
回答 3
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
方式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
回答 4
我强烈建议该方法提取“污染物”列:
df_pollutants = pd.DataFrame(df['Pollutants'].values.tolist(), index=df.index)
它比
df_pollutants = df['Pollutants'].apply(pd.Series)
当df的大小很大时。
回答 5
你可以用join
与pop
+ tolist
。性能concat
与drop
+ 相当tolist
,但有些人可能会发现此语法更简洁:
res = df.join(pd.DataFrame(df.pop('b').tolist()))
使用其他方法进行基准测试:
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
回答 6
一种解决方案如下:
>>> 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
回答 7
my_df = pd.DataFrame.from_dict(my_dict, orient='index', columns=['my_col'])
..本可以正确解析字典(将每个字典键放入单独的df列中,并将键值放入df行中),因此这些dict首先不会被压入单个列中。
回答 8
我将这些步骤串联在一个方法中,您只需要传递数据框和包含扩展字典的列即可:
def expand_dataframe(dw: pd.DataFrame, column_to_expand: str) -> pd.DataFrame:
"""
dw: DataFrame with some column which contain a dict to expand
in columns
column_to_expand: String with column name of dw
"""
import pandas as pd
def convert_to_dict(sequence: str) -> Dict:
import json
s = sequence
json_acceptable_string = s.replace("'", "\"")
d = json.loads(json_acceptable_string)
return d
expanded_dataframe = pd.concat([dw.drop([column_to_expand], axis=1),
dw[column_to_expand]
.apply(convert_to_dict)
.apply(pd.Series)],
axis=1)
return expanded_dataframe
回答 9
df = pd.concat([df['a'], df.b.apply(pd.Series)], axis=1)