解析使用Pandas从CSV加载的JSON字符串

pro*_*uga 32 python pandas

我正在使用CSV文件,其中几个列有一个简单的json对象(几个键值对),而其他列是正常的.这是一个例子:

name,dob,stats
john smith,1/1/1980,"{""eye_color"": ""brown"", ""height"": 160, ""weight"": 76}"
dave jones,2/2/1981,"{""eye_color"": ""blue"", ""height"": 170, ""weight"": 85}"
bob roberts,3/3/1982,"{""eye_color"": ""green"", ""height"": 180, ""weight"": 94}"
Run Code Online (Sandbox Code Playgroud)

使用后df = pandas.read_csv('file.csv'),解析stats列并将其拆分为其他列的最有效方法是什么?

大约一个小时后,我唯一能想到的是:

import json
stdf = df['stats'].apply(json.loads)
stlst = list(stdf)
stjson = json.dumps(stlst)
df.join(pandas.read_json(stjson))
Run Code Online (Sandbox Code Playgroud)

这似乎我做错了,考虑到我需要定期在三个列上执行此操作,这是相当多的工作.

*编辑:所需的输出是下面的数据框对象.添加以下代码行以我的(糟糕的)方式:

df = df.join(pandas.read_json(stjson))
del(df['stats'])
In [14]: df

Out[14]:
          name       dob eye_color  height  weight
0   john smith  1/1/1980     brown     160      76
1   dave jones  2/2/1981      blue     170      85
2  bob roberts  3/3/1982     green     180      94
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 30

有一种稍微简单的方法,但最终你必须调用json.loads pandas.read_csv中有一个转换器的概念

converters : dict. optional

Dict of functions for converting values in certain columns. Keys can either be integers or column labels
Run Code Online (Sandbox Code Playgroud)

首先定义自定义解析器.在这种情况下,下面应该工作:

def CustomParser(data):
    import json
    j1 = json.loads(data)
    return j1
Run Code Online (Sandbox Code Playgroud)

在你的情况下你会有类似的东西:

df = pandas.read_csv(f1, converters={'stats':CustomParser},header=0)
Run Code Online (Sandbox Code Playgroud)

我们告诉read_csv以标准方式读取数据,但对于stats列,请使用我们的自定义解析器.这将使统计列成为一个字典

从这里开始,我们可以使用一点hack直接将这些列与适当的列名一起附加到一个步骤中.这只适用于常规数据(json对象需要有3个值或至少需要在CustomParser中处理缺失值)

df[sorted(df['stats'][0].keys())] = df['stats'].apply(pandas.Series)
Run Code Online (Sandbox Code Playgroud)

在左侧,我们从stats列的元素的键中获取新的列名.stats列中的每个元素都是字典.所以我们正在进行批量分配.在右侧,我们使用apply打破'stats'列,从每个键/值对中创建一个数据帧.

  • 我会`导入json`然后使用:`pandas.read_csv(f1,converters = {'stats':json.loads})`.您不需要定义新函数,并且您绝对不需要在其中导入. (4认同)
  • 你好.我在Python 3中尝试了这个并得到了错误:ValueError:列的长度必须与key相同.除了我在JSON中嵌套了值之外,我的要求和预期输出完全相同. (2认同)

jor*_*ris 24

我认为应用这json.load是一个好主意,但从那里你可以直接将其转换为数据帧列,而不是再次写入/加载它:

stdf = df['stats'].apply(json.loads)
pd.DataFrame(stdf.tolist()) # or stdf.apply(pd.Series)
Run Code Online (Sandbox Code Playgroud)

或者在一个步骤中:

df.join(df['stats'].apply(json.loads).apply(pd.Series))
Run Code Online (Sandbox Code Playgroud)


abe*_*bop 6

Paul的答案非常好但一般不正确,因为无法保证列的排序在最后一行的左侧和右侧是相同的.(事实上​​,它似乎不能解决问题中的测试数据,而是错误地切换高度和重量列.)

我们可以通过确保对LHS上的dict键列表进行排序来解决这个问题.这是有效的,因为applyRHS上的索引会自动按索引排序,在这种情况下,索引是列名列表.

def CustomParser(data):
  import json
  j1 = json.loads(data)
  return j1

df = pandas.read_csv(f1, converters={'stats':CustomParser},header=0)
df[sorted(df['stats'][0].keys())] = df['stats'].apply(pandas.Series)
Run Code Online (Sandbox Code Playgroud)

  • Thx发现这一点。我已将您的回答进行了更新,以确保完整性 (2认同)

Gle*_*son 5

选项1

如果在将列json.dumps写入 csv 之前将其转储,则可以使用以下命令重新读取:

import json
import pandas as pd

df = pd.read_csv('data/file.csv', converters={'json_column_name': json.loads})
Run Code Online (Sandbox Code Playgroud)

选项 2

如果你没有,那么你可能需要使用这个:

import json
import pandas as pd

df = pd.read_csv('data/file.csv', converters={'json_column_name': eval})
Run Code Online (Sandbox Code Playgroud)

选项 3

对于更复杂的情况,您可以编写这样的自定义转换器:

import json
import pandas as pd

def parse_column(data):
    try:
        return json.loads(data)
    except Exception as e:
        print(e)
        return None


df = pd.read_csv('data/file.csv', converters={'json_column_name': parse_column})
Run Code Online (Sandbox Code Playgroud)