循环转换/提取pandas DataFrame中的json数据不起作用

Posted

技术标签:

【中文标题】循环转换/提取pandas DataFrame中的json数据不起作用【英文标题】:Loop to convert/extract json data in pandas DataFrame not working 【发布时间】:2019-11-14 12:28:57 【问题描述】:

我正在尝试执行 EDA 演练,并通过使用循环处理包含 json 数据的列来转换/提取数据框中的 json 数据。我这样做是通过为循环设置一个列表进行迭代,然后设置 for 循环以加载 json 数据并为每列的每一行提取名称字段。

在处理列表中的第一列后,它会抛出一个“JSON 对象必须是 str、bytes 或 bytearray,而不是 'list'”错误。

我尝试通过添加和删除列来修改列表以查看失败的位置,它始终适用于第一列,但之后放弃。对于一列的列表,这仍然适用。

我认为问题在于正在传递的“json.loads(data)”以某种方式仍然指向最后一个循环的结果(因为最后一个循环中的 json 被转换/提取到一个列表中)。但我不确定是否是这种情况,如果是,如何解决。

代码如下:

json_fields = ['genres', 'production_countries', 'spoken_languages']

for field in json_fields:
    print(field)
    movies_df[field] = movies_df[field].apply(lambda data:[row['name'] for row in json.loads(data)])

这是回溯:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-cc13bd0423f3> in <module>()
      3 for field in json_fields:
      4     print(field)
----> 5     movies_df[field] = movies_df[field].apply(lambda data:[row['name'] for row in json.loads(data)])

~/anaconda3/lib/python3.6/site-packages/pandas/core/series.py in apply(self, func, convert_dtype, args, **kwds)
   2549             else:
   2550                 values = self.asobject
-> 2551                 mapped = lib.map_infer(values, f, convert=convert_dtype)
   2552 
   2553         if len(mapped) and isinstance(mapped[0], Series):

pandas/_libs/src/inference.pyx in pandas._libs.lib.map_infer()

<ipython-input-15-cc13bd0423f3> in <lambda>(data)
      3 for field in json_fields:
      4     print(field)
----> 5     movies_df[field] = movies_df[field].apply(lambda data:[row['name'] for row in json.loads(data)])

~/anaconda3/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    346         if not isinstance(s, (bytes, bytearray)):
    347             raise TypeError('the JSON object must be str, bytes or bytearray, '
--> 348                             'not !r'.format(s.__class__.__name__))
    349         s = s.decode(detect_encoding(s), 'surrogatepass')
    350 

TypeError: the JSON object must be str, bytes or bytearray, not 'list'

这里是结果表的链接: https://imgur.com/a/yHDdFM8

Genres 是有效的列,另外两个是无效的列示例

编辑:这是我正在使用的表格的来源:https://www.kaggle.com/tmdb/tmdb-movie-metadata/

【问题讨论】:

您的 lambda 表达式为每一行返回 [row['name'] for row in json.loads(data)]。我猜您更希望将每个文档的名称分配给另一行,对吧? @jottbe,我很难理解您所说的文档是什么意思。您的意思是“数据”是指每行中包含的数据,因为它通过 for 循环? 那么目标是将图像中每个突出显示的列转换为名称列表而不是字典吗? @exlo,文档是指 json 文档(与行相同)。很抱歉混淆了这些条款。 @exlo 你能补充一下你在数据框中的阅读方式吗,当我使用movies_df = pandas.Dataframe.from_csv(filename) 然后运行相同的代码时,我没有遇到同样的问题。 【参考方案1】:

所以我认为“genre”列是表示 JSON 的字符串,而“production_countries”和“spoken_languages”列是包含 JSON 的字符串表示或已被解析为 python 字典的列表。

尝试将循环更改为

for field in json_fields:
    print(field)
    print(type(movies_df[field][0]))
    print(type(movies_df[field][0][0]))

根据输出有几个解决方案

1。 “production_countries”和“spoken_languages”是字符串列表

如果上面的循环给你类似的东西

production_countries
<class 'list'>
<class 'str'>

那么“production_countries”中的每一行都是一个列表,列表中的每个元素都是一个字符串,应该可以使用以下代码将其解析为JSON。

for field in json_fields:
    if field == 'genres':
        movies_df[field] = movies_df[field].apply(lambda data: [row['name'] for row in json.loads(data)])
    elif field == 'production_countries':
        movies_df[field] = movies_df[field].apply(lambda data: [json.loads(row)['name'] for row in data])

2。 "production_countries" 和 "spoken_languages" 是 python 字典列表

如果第一个循环给你类似的东西

production_countries
<class 'list'>
<class 'dict'>

那么“production_countries”上的每一行都是一个列表,列表中的每个元素都是一个字典。那么以下应该可以工作

for field in json_fields:
    if field == 'genres':
        movies_df[field] = movies_df[field].apply(lambda data: [row['name'] for row in json.loads(data)])
    elif field == 'production_countries':
        movies_df[field] = movies_df[field].apply(lambda data: [row['name'] for row in data])

总结

如果上述方法不起作用,则列可能由其他数据结构组成。如果上述方法确实有效,最好更改数据加载到 panda 数据帧的方式,而不是使用上述解决方案。

【讨论】:

感谢调试提示!看起来 type 的输出实际上是 'production_countries '。对于“流派”领域也是如此。两种解决方案都导致错误(解决方案 1 引发“预期值:第 1 行第 2 列(字符 1)”错误),解决方案 2 显示与我的原始帖子相同的 json 字符串错误。 有趣,您有用于制作数据帧的源 JSON 或我可以测试的 REST 端点吗? @exlo 是的!可以在这里找到:kaggle.com/tmdb/tmdb-movie-metadata。我也会更新我的问题的正文。

以上是关于循环转换/提取pandas DataFrame中的json数据不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Pandas Dataframe 中的字符串转换为列表或字符数组?

从 pandas Dataframe 中提取月份数据

在 for 循环中将 Python Dask 系列转换为列表或 Dask DataFrame

pandas DataFrame 中的自定义浮点格式

从 pandas DataFrame 中的列中提取 JSON 数据

使用 For 循环修改 Pandas 中的 DataFrame 字典