【问题标题】:Add ID found in list to new column in pandas dataframe将列表中的 ID 添加到 pandas 数据框中的新列
【发布时间】:2020-07-14 07:49:29
【问题描述】:

假设我有以下数据框(一列整数和一列整数列表)...

      ID                   Found_IDs
0  12345        [15443, 15533, 3433]
1  15533  [2234, 16608, 12002, 7654]
2   6789      [43322, 876544, 36789]

还有一个单独的 ID 列表...

bad_ids = [15533, 876544, 36789, 11111]

鉴于此,忽略df['ID'] 列和任何索引,我想看看bad_ids 列表中的任何ID 是否在df['Found_IDs'] 列中被提及。我到目前为止的代码是:

df['bad_id'] = [c in l for c, l in zip(bad_ids, df['Found_IDs'])]

这有效,但前提是bad_ids 列表比数据框长,而对于真实数据集,bad_ids 列表将比数据框短很多。如果我将bad_ids 列表设置为只有两个元素...

bad_ids = [15533, 876544]

我收到一个非常流行的错误(我已经阅读了许多具有相同错误的问题)...

ValueError: Length of values does not match length of index

我尝试将列表转换为系列(错误没有变化)。在执行理解行之前,我还尝试添加新列并将所有值设置为 False(同样没有改变错误)。

两个问题:

  1. 如何让我的代码(如下)适用于比 数据框?
  2. 如何获取代码以写入找到的实际 ID 回到df['bad_id'] 列(比真/假更有用)?

bad_ids = [15533, 876544] 的预期输出:

      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]    True
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]    True

bad_ids = [15533, 876544] 的理想输出(ID 被写入一个或多个新列):

      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]    15533
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]    876544

代码:

import pandas as pd

result_list = [[12345,[15443,15533,3433]],
        [15533,[2234,16608,12002,7654]],
        [6789,[43322,876544,36789]]]

df = pd.DataFrame(result_list,columns=['ID','Found_IDs'])

# works if list has four elements
# bad_ids = [15533, 876544, 36789, 11111]

# fails if list has two elements (less elements than the dataframe)
# ValueError: Length of values does not match length of index
bad_ids = [15533, 876544]

# coverting to Series doesn't change things
# bad_ids = pd.Series(bad_ids)
# print(type(bad_ids))

# setting up a new column of false values doesn't change things
# df['bad_id'] = False

print(df)

df['bad_id'] = [c in l for c, l in zip(bad_ids, df['Found_IDs'])]

print(bad_ids)

print(df)

【问题讨论】:

    标签: python python-3.x pandas dataframe


    【解决方案1】:

    使用np.intersect1d获取两个列表的交集:

    df['bad_id'] = df['Found_IDs'].apply(lambda x: np.intersect1d(x, bad_ids))
    
          ID                   Found_IDs    bad_id
    0  12345        [15443, 15533, 3433]   [15533]
    1  15533  [2234, 16608, 12002, 7654]        []
    2   6789      [43322, 876544, 36789]  [876544]
    

    或者只是使用sets的交集的香草python:

    bad_ids_set = set(bad_ids)
    df['Found_IDs'].apply(lambda x: list(set(x) & bad_ids_set))
    

    【讨论】:

      【解决方案2】:

      如果想用bad_ids 的所有值测试Found_IDs 列中列表的所有值,请使用:

      bad_ids = [15533, 876544]
      
      df['bad_id'] = [any(c in l for c in bad_ids) for l  in df['Found_IDs']]
      print (df)
            ID                   Found_IDs  bad_id
      0  12345        [15443, 15533, 3433]    True
      1  15533  [2234, 16608, 12002, 7654]   False
      2   6789      [43322, 876544, 36789]    True
      

      如果想要全部匹配:

      df['bad_id'] = [[c for c in bad_ids if c in l] for l  in df['Found_IDs']]
      print (df)
            ID                   Found_IDs    bad_id
      0  12345        [15443, 15533, 3433]   [15533]
      1  15533  [2234, 16608, 12002, 7654]        []
      2   6789      [43322, 876544, 36789]  [876544]
      

      对于第一次匹配,如果设置了空列表False,可能的解决方案,但不建议将布尔值和数字混合:

      df['bad_id'] = [next(iter([c for c in bad_ids if c in l]), False) for l  in df['Found_IDs']]
      print (df)
            ID                   Found_IDs  bad_id
      0  12345        [15443, 15533, 3433]   15533
      1  15533  [2234, 16608, 12002, 7654]   False
      2   6789      [43322, 876544, 36789]  876544
      

      集合解决方案:

      df['bad_id'] = df['Found_IDs'].map(set(bad_ids).intersection)
      print (df)
      
            ID                   Found_IDs    bad_id
      0  12345        [15443, 15533, 3433]   {15533}
      1  15533  [2234, 16608, 12002, 7654]        {}
      2   6789      [43322, 876544, 36789]  {876544}
      

      也与列表理解类似:

      df['bad_id'] = [list(set(bad_ids).intersection(l)) for l  in df['Found_IDs']]
      print (df)
            ID                   Found_IDs    bad_id
      0  12345        [15443, 15533, 3433]   [15533]
      1  15533  [2234, 16608, 12002, 7654]        []
      2   6789      [43322, 876544, 36789]  [876544]
      

      【讨论】:

        【解决方案3】:

        您可以申请和使用np.any:

        df['bad_id'] = df['Found_IDs'].apply(lambda x: np.any([c in x for c in bad_ids]))
        

        如果在 Found_IDs 中存在 bad_id,则返回 bool,如果您想检索此 bad_ids:

        df['bad_id'] = df['Found_IDs'].apply(lambda x: [*filter(lambda x: c in x, bad_ids)])
        

        这将返回found_ids处的bad_ids列表,如果有0则返回[]

        【讨论】:

          【解决方案4】:

          使用merge 和concat 同时按您的索引分组以返回所有匹配项。

          bad_ids = [15533, 876544, 36789, 11111]
          
          df2 = pd.concat(
              [
                  df,
                  pd.merge(
                      df["Found_IDs"].explode().reset_index(),
                      pd.Series(bad_ids, name="bad_ids"),
                      left_on="Found_IDs",
                      right_on="bad_ids",
                      how="inner",
                  )
                  .groupby("index")
                  .agg(bad_ids=("bad_ids", list)),
              ],
              axis=1,
          ).fillna(False)
          print(df2)
          
          
                ID                   Found_IDs          bad_ids
          0  12345        [15443, 15533, 3433]          [15533]
          1  15533  [2234, 16608, 12002, 7654]            False
          2   6789      [43322, 876544, 36789]  [876544, 36789]
          

          【讨论】:

            【解决方案5】:

            使用explode和groupby聚合

            s = df['Found_IDs'].explode()
            df['bad_ids'] = s.isin(bad_ids).groupby(s.index).any()
            

            对于bad_ids = [15533, 876544]

            >>> df
                  ID                   Found_IDs  bad_ids
            0  12345        [15443, 15533, 3433]     True
            1  15533  [2234, 16608, 12002, 7654]    False
            2   6789      [43322, 876544, 36789]     True
            

            或

            用于获取匹配的值

            s = df['Found_IDs'].explode()
            s.where(s.isin(bad_ids)).groupby(s.index).agg(lambda x: list(x.dropna()))
            

            对于bad_ids = [15533, 876544]

                  ID                   Found_IDs   bad_ids
            0  12345        [15443, 15533, 3433]   [15533]
            1  15533  [2234, 16608, 12002, 7654]        []
            2   6789      [43322, 876544, 36789]  [876544]
            

            【讨论】:

              猜你喜欢
              • 2017-05-02
              • 2017-03-18
              • 1970-01-01
              • 1970-01-01
              • 2018-02-17
              • 1970-01-01
              • 2021-12-16
              • 2021-06-26
              • 2018-10-04
              相关资源
              最近更新 更多