【问题标题】:Parse a column of stringified dictionaries in pandas在 pandas 中解析一列字符串化字典
【发布时间】:2020-04-01 22:56:48
【问题描述】:

我有这个数据框:

df = pd.DataFrame([
  { "name": "foo", "results": "{'lower_bound': '15000', 'upper_bound': '19999'}"},
  { "name": "bar", "results": "{'lower_bound': '10000', 'upper_bound': '14999'}"}
])

目前看起来是这样的:

    name    results
0   foo     {'lower_bound': '15000', 'upper_bound': '19999'}
1   bar     {'lower_bound': '10000', 'upper_bound': '14999'}

我想将results 列转换为结构化数据,以便过滤 df.lower_bound 小于 13000 的行。

我该怎么做?

【问题讨论】:

  • 未经测试,但我认为您可以使用 from pandas.io.json import json_normalize 然后在您的列上调用该函数,explode 是另一个您可以查看的函数
  • 谢谢。第一步可能是从字符串字段转换它们?
  • 你是对的,谢谢!你不经意间教会了我一些新东西。

标签: python pandas


【解决方案1】:

这可能不是最佳或最佳方式:

from ast import literal_eval
df['results'] = df['results'].apply(lambda i: literal_eval(i))

这会将'results' 列中的字符串格式值转换为字典。输出:

  name                                           results
0  foo  {'lower_bound': '15000', 'upper_bound': '19999'}
1  bar  {'lower_bound': '10000', 'upper_bound': '14999'}

然后:

df['results'] = df['results'].apply(lambda i: {k:int(v) for k,v in i.items()})

这会将该列中字典的值从字符串转换为整数格式。输出:

  name                                       results
0  foo  {'lower_bound': 15000, 'upper_bound': 19999}
1  bar  {'lower_bound': 10000, 'upper_bound': 14999}

现在,获取字典的 lower_bound 键的值 lower_bound 键,也要处理这种情况:

df.loc[map(lambda i:i.get('lower_bound', '0')<13000, df['results'])]

输出:

  name                                       results
1  bar  {'lower_bound': 10000, 'upper_bound': 14999}

【讨论】:

    【解决方案2】:

    如果你不想修改你现有的DataFrame,你可以通过将“结果”转换为DataFrame来构建条件,然后过滤:

    mask = (pd.DataFrame(df['results'].apply(ast.literal_eval).tolist())
              .astype(int)
              .eval('lower_bound < 13000'))
    df[mask]
    
      name                                           results
    1  bar  {'lower_bound': '10000', 'upper_bound': '14999'}
    

    另一种选择是通过concat 将其分配回DataFrame:

    df = pd.concat([
          df, 
          pd.DataFrame(df.pop('results').apply(ast.literal_eval).tolist()).astype(int)
      ], 
      axis=1)
    df[df['lower_bound'] < 13000]
    
      name lower_bound upper_bound
    1  bar       10000       14999
    

    【讨论】:

      【解决方案3】:

      另一种方法是使用 json_normalize 和 ast_literal

      from pandas.io.json import json_normalize
      from ast import literal_eval
      

      然后拆分 JSON col,然后将它们合并回它们的索引。

          s = json_normalize(df['results'].apply(literal_eval).astype(int))
          df_new = pd.merge(df,s,right_index=True,left_index=True)
          print(df_new)
            name  Results                                           lower_bound  upper_bound  
          0  foo  {'lower_bound': '15000', 'upper_bound': '19999'}       15000   19999  
          1  bar  {'lower_bound': '10000', 'upper_bound': '14999'}       10000   14999  
      

      对于数据类型:

        print(df_new.dtypes)
          name           object
          results        object
          lower_bound     int32
          upper_bound     int32
          dtype: object
      

      【讨论】:

        【解决方案4】:

        您可以通过将单引号替换为双引号将字符串表达式转换为 JSON 兼容格式,然后从 JSON 转换为 dict:

        df = pd.DataFrame([
          { "name": "foo", "results": "{'lower_bound': '15000', 'upper_bound': '19999'}"},
          { "name": "bar", "results": "{'lower_bound': '10000', 'upper_bound': '14999'}"}
        ])
        pd.concat([df, pd.DataFrame(df.pop('results').apply(lambda x: json.loads(x.replace("'", '"'))).tolist())], axis=1)
        

        这是faster,而不是使用ast.literal_eval,如果您有很多行,您将清楚地看到执行时间的差异。

        【讨论】:

          【解决方案5】:

          你可以试试:

          df = pd.DataFrame([
            { "name": "foo", "results": "{'lower_bound': '15000', 'upper_bound': '19999'}"},
            { "name": "bar", "results": "{'lower_bound': '10000', 'upper_bound': '14999'}"}
          ])
          lower_bound = []
          upper_bound = []
          for index, row in df.iterrows():
              r = eval(row['results'])
              lower_bound.append(r['lower_bound'])
              upper_bound.append(r['upper_bound'])
          df['lower_bound'] = lower_bound
          df['upper_bound'] = upper_bound
          print(df[['name', 'lower_bound', 'upper_bound']])
          

          结果:

            name lower_bound upper_bound
          0  foo       15000       19999
          1  bar       10000       14999
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-03-18
            • 1970-01-01
            • 2012-06-30
            • 1970-01-01
            • 2015-06-13
            • 1970-01-01
            • 2018-02-20
            • 2018-03-02
            相关资源
            最近更新 更多