【问题标题】:Extract values belonging to a specific key from a column of JSON in pandas从 pandas 中的 JSON 列中提取属于特定键的值
【发布时间】:2019-05-23 10:03:00
【问题描述】:

例如,我有一个如下所示的数据框:

    name      eventlist
0   a         [{'t': '1234', 'n': 'user_engagem1'},{'t': '2345', 'n': 'user_engagem2'},{'t': '3456', 'n': 'user_engagem3'}]
1   b         [{'t': '2345', 'n': 'user_engagem4'},{'t': '1345', 'n': 'user_engagem5'},{'t': '1356', 'n': 'user_engagem6'},{'t': '1345', 'n': 'user_engagem5'},{'t': '1359', 'n': 'user_engagem6'}]
2   c         [{'t': '1334', 'n': 'user_engagem3'},{'t': '2345', 'n': 'user_engagem4'},{'t': '3556', 'n': 'user_engagem2'}]

我用 re.findall 尝试了一个字符串,它似乎有效,我得到了类似的结果 ['1234', '2345', '3456'],但我无法将其应用到数据帧中

#code 1,apply to string successfully
str="[{'t': '1234', 'n': 'user_engagem'},{'t': '2345', 'n': 'user_engagem'},{'t': '3456', 'n': 'user_engagem'}]"
print(re.findall(r"t': '(.+?)', '", str))

#code 2,apply to dateframe doesn't work
df['t']=df['events'].str.findall(r"t': '(.+?)', '", df['events'])
print(list)

我想得到类似的结果

    name      eventlist

0   a         ['1234', '2345', '3456']
1   b         ['2345', '1345','1234','1356', '1356']
2   c         ['1334', '2345', '3556']

甚至更好,我可以得到类似的结果

    name      t_first       t_last
0   a         1234           3456
1   b         2345           1359
2   c         1334           3556

【问题讨论】:

    标签: python json regex pandas dictionary


    【解决方案1】:

    您可以使用ast.literal_eval 转换字典列表,然后使用keys 通过t 获取值:

    import ast
    
    out = []
    for x in df.pop('eventlist'):
        a = ast.literal_eval(x)
        out.append([a[0].get('t'), a[-1].get('t')])
    

    或者使用re.findall:

    out = []
    for x in df.pop('eventlist'):
        a = re.findall(r"t': '(.+?)', '", x)
        out.append([a[0], a[-1]])
    

    print (out)
    [['1234', '3456'], ['2345', '1359'], ['1334', '3556']]
    

    然后将DataFramejoin创建为原始:

    df = df.join(pd.DataFrame(out, columns=['t_first','t_last'], index=df.index))
    print (df)
      name t_first t_last
    0    a    1234   3456
    1    b    2345   1359
    2    c    1334   3556
    

    findallassign 的新列的另一个解决方案:

    a = df.pop('eventlist').str.findall(r"t': '(.+?)'")
    df = df.assign(t_first= a.str[0], t_last = a.str[-1])
    

    【讨论】:

      【解决方案2】:

      str.findall 需要一个参数:正则表达式模式。

      # Call `pop` here to remove the "events" column.
      v = df.pop('eventlist').str.findall(r"t': '(.+?)'")
      print(v)
      
      0                [1234, 2345, 3456]
      1    [2345, 1345, 1356, 1345, 1359]
      2                [1334, 2345, 3556]
      Name: events, dtype: object
      

      然后您可以将其加载到单独的列中:

      # More efficient than assigning if done in-place. 
      df['t_first'] = v.str[0]
      df['t_last'] = v.str[-1]
      # Or, if you want to return a copy,
      # df = df.assign(t_first=v.str[0], t_last=v.str[-1])
      
      df
      
        name t_first t_last
      0    a    1234   3456
      1    b    2345   1359
      2    c    1334   3556
      

      另一个更好的选择是使用re.compile 预编译您的模式并在循环中运行它,从findall 结果中提取第一个和最后一个项目。

      import re
      
      p = re.compile(r"t': '(.+?)'")
      out = []
      for name, string in zip(df.name, df.pop('eventlist')):
          a = p.findall(string)
          out.append([name, a[0], a[-1]])
      
      pd.DataFrame(out, columns=['name', 't_first','t_last'], index=df.index)
      
        name t_first t_last
      0    a    1234   3456
      1    b    2345   1359
      2    c    1334   3556
      

      如果您需要将它们转换为 int,请将 out.append([name, a[0], a[-1]]) 替换为 out.append([name, int(a[0]), int(a[-1])])


      上述解决方案假定您将始终拥有多个匹配项。如果可能只有一个匹配项或没有匹配项,您可以通过检查附加到count 的匹配项数来修改解决方案。

      p = re.compile(r"t': '(.+?)'")
      out = []
      for name, string in zip(df.name, df.pop('eventlist')):
          first = second = np.nan
          if pd.notna(string):
              a = p.findall(string)
              if len(a) > 0:
                  first = int(a[0])
                  second = int(a[-1]) if len(a) > 1 else second
      
          out.append([name, first, second])
      
      pd.DataFrame(out, columns=['name', 't_first','t_last'], index=df.index)
      
        name  t_first  t_last
      0    a     1234    3456
      1    b     2345    1359
      2    c     1334    3556
      

      【讨论】:

        【解决方案3】:
        df['eventlist'] = df['eventlist'].map(lambda x:[i['t'] for i in x])
        df
             name                       eventlist
           0    a              [1234, 2345, 3456]
           1    b  [2345, 1345, 1356, 1345, 1359]
           2    c              [1334, 2345, 3556]
        
        df['t_first'] = df['eventlist'][0]
        df['t_last']=df['eventlist'].map(lambda x:x[len(x)-1])
        df = df[['name','t_first','t_last']]
        df
             name t_first t_last
           0    a    1234   3456
           1    b    2345   1359
           2    c    3456   3556
        

        【讨论】:

          猜你喜欢
          • 2022-01-16
          • 2023-03-16
          • 1970-01-01
          • 1970-01-01
          • 2021-02-08
          • 1970-01-01
          • 1970-01-01
          • 2023-01-24
          • 2019-06-12
          相关资源
          最近更新 更多