首页 > 解决方案 > 熊猫读取 csv 忽略 " 字符

问题描述

我有一个用字符分号(;)分隔的 CSV 数据框。我的数据框中的几列是 JSON 类型,我想在后面的部分将它们转换为 python dict。

CSV file <tmp.csv>:

7.384906;7.3849072;2;0.2708226296521021;0;0;0;;
9.05233;9.05192;5;0.5523690040041611;1;0;0;"{\"EMAIL_SENT\": 1}";"{\"-1\": 1}"
3.6593602;3.6593602;2;0.5465436324626254;0;0;0;;
3.5134177;3.5130887;2;0.5692996018584914;0;0;0;;
0.6824124;0.6824124;2;2.9307791611130423;0;0;0;"{\"STAGE_CHANGE\": 1, \"CREATE\": 1}";"{\"-1\": 2}"

当我将此文件读入熊猫时,

>>> df = pd.read_csv("tmp.csv", sep=";", header=None)
>>> df

          0         1  2         3  4  5  6                                     7            8
0  7.384906  7.384907  2  0.270823  0  0  0                                   NaN          NaN
1  9.052330  9.051920  5  0.552369  1  0  0                   {\EMAIL_SENT\": 1}"  {\-1\": 1}"
2  3.659360  3.659360  2  0.546544  0  0  0                                   NaN          NaN
3  3.513418  3.513089  2  0.569300  0  0  0                                   NaN          NaN
4  0.682412  0.682412  2  2.930779  0  0  0  {\STAGE_CHANGE\": 1, \"CREATE\": 1}"  {\-1\": 2}"

如此处所示,我的 JSON 列未正确读取。我在每个 JSON 的开头都缺少字符 "。因此,我无法将其转换为 python dict。

>> df[8].apply(lambda elem: {} if pd.isna(elem) else json.loads(elem))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/anaconda3/envs/forecasting/lib/python3.7/site-packages/pandas/core/series.py", line 3848, in apply
    mapped = lib.map_infer(values, f, convert=convert_dtype)
  File "pandas/_libs/lib.pyx", line 2329, in pandas._libs.lib.map_infer
  File "<stdin>", line 1, in <lambda>
  File "/opt/anaconda3/envs/forecasting/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/opt/anaconda3/envs/forecasting/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/opt/anaconda3/envs/forecasting/lib/python3.7/json/decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

请让我知道如何解决这个问题。

标签: pythonjsonpandasdataframecsv

解决方案


在读取 csv 时不使用引号,然后在将字符串加载到 json 之前去掉前导/尾随双引号。

df = pd.read_csv("tmp.csv", sep=";", header=None, quoting=3)
df[7].apply(lambda elem: {} if pd.isna(elem) else json.loads(elem.strip('"')))
df[8].apply(lambda elem: {} if pd.isna(elem) else json.loads(elem.strip('"')))

结果:

          0         1  2  ...  6                                 7          8
0  7.384906  7.384907  2  ...  0                                {}         {}
1  9.052330  9.051920  5  ...  0                 {'EMAIL_SENT': 1}  {'-1': 1}
2  3.659360  3.659360  2  ...  0                                {}         {}
3  3.513418  3.513089  2  ...  0                                {}         {}
4  0.682412  0.682412  2  ...  0  {'STAGE_CHANGE': 1, 'CREATE': 1}  {'-1': 2}


更新json字符串包含分隔符的情况
我没有通用解决方案,在您的示例中只有一个(相当丑陋的)解决方法。如果您知道 json 字符串在最后一列中,则可以使用保证不在字符串中的分隔符将 csv 作为一列读取,然后将第一列拆分为实际分隔符,将 json 列拆分为实际用双引号括起来的分隔符。从这里您可以像以前一样继续,只有空列''而不是NA.

例子:

df = pd.read_csv('tmp.csv', sep='\n', header=None)[0].str.split(';', 7, expand=True)
df[[7,8]] = df[7].str.split('";"|^;$', expand=True)
df[7] = df[7].apply(lambda elem: {} if elem == '' else json.loads(elem.strip('"').replace('\\"', '"')))
df[8] = df[8].apply(lambda elem: {} if elem == '' else json.loads(elem.strip('"').replace('\\"', '"')))

结果:

           0          1  2                   3  4  5  6                                     7          8
0   7.384906  7.3849072  2  0.2708226296521021  0  0  0                                    {}         {}
1    9.05233    9.05192  5  0.5523690040041611  1  0  0                     {'EMAIL_SENT': 1}  {'-1': 1}
2  3.6593602  3.6593602  2  0.5465436324626254  0  0  0                                    {}         {}
3  3.5134177  3.5130887  2  0.5692996018584914  0  0  0                                    {}         {}
4  0.6824124  0.6824124  2  2.9307791611130423  0  0  0  {'STAGE_CHANGE': 1, 'CREATE; HA': 1}  {'-1': 2}

推荐阅读