【问题标题】:Filter one data frame based on other data frame in pandas根据 pandas 中的另一个数据框过滤一个数据框
【发布时间】:2019-11-07 03:10:20
【问题描述】:

我在 pandas 中有两个 DataFrame:

import pandas as pd

df1 = pd.DataFrame({'Name': ["A", "B", "C", "C","D","D","E"],
                   'start': [50, 124, 1, 159, 12, 26,110],
                   'stop': [60, 200, 19, 200, 24, 30,160]})
df2 = pd.DataFrame({'Name': ["B", "C","D","E"],
                   'start': [126, 143, 19, 159],
                   'stop': [129, 220, 27, 200]})

print(df1)

  Name  start  stop
0    A     50    60
1    B    124   200
2    C      1    19
3    C    159   200
4    D     12    24
5    D     26    30
6    E    110   160

print(df2)

  Name  start  stop
0    B    126   129
1    C    143   220
2    D     19    27
3    E    159   200

我想使用以下条件过滤 df1 以删除基于 df2 的行:

  1. 名称应同时出现在 df1 和 df2 中
  2. 名称从开始到停止的范围与另一个 DataFrame 中该名称的从开始到停止的范围重叠

这将给出:

  Name  start  stop
0    B    124   200
1    C    159   200
2    D     12    24
3    D     26    30
4    E    110   160

地点:

  • A 已被删除,因为 df2 中没有 A
  • B 被保留,因为 df2 中 B 的起点和终点嵌套在 df1 中 B 的起点和终点中
  • df1 的一个 C 已被删除,因为它的值没有与 df2 重叠,而另一个被保留,因为它嵌套在 df2 中 C 的开始和停止范围内
  • 保留两个 D,因为它们都与 df2 中的 D 范围重叠
  • E 被保留,因为它的范围与 df2 中的 E 重叠

任何帮助将不胜感激!

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    为了解决您的问题,我应用了一种类似于 SQL 的方式来模仿以下查询:

    SELECT
      df.Name, df.start_x AS start, df.stop_x AS stop
    FROM (
      SELECT
        df1.Name, df1.start AS start_x, df1.stop AS stop_x,
                  df2.start AS start_y, df2.stop AS stop_y
        FROM df1
        INNER JOIN df2
          ON df1.Name = df2.Name
    ) AS df
    WHERE (df.stop_y >= df.start_x) AND (df.stop_x >= df.start_y)
    

    此查询已转换为以下使用pandas.merge 方法的代码片段。请注意,您必须在表达式(df.stop_y> = df.start_x) & (df.stop_x> = df.start_y) 中使用括号。没有它们,代码会抛出异常

    ValueError:Series 的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。

    import pandas as pd
    
    df1 = pd.DataFrame({'Name': ["A", "B", "C", "C","D","D","E"],
                       'start': [50, 124, 1, 159, 12, 26,110],
                       'stop': [60, 200, 19, 200, 24, 30,160]})
    df2 = pd.DataFrame({'Name': ["B", "C","D","E"],
                       'start': [126, 143, 19, 159],
                       'stop': [129, 220, 27, 200]})
    df = pd.merge(df1, df2, on=['Name'])
    df = df[(df.stop_y >= df.start_x) & (df.stop_x >= df.start_y)]
    df.rename(columns={'start_x':'start', 'stop_x':'stop'}, inplace=True)
    df.drop(['start_y', 'stop_y'], axis=1, inplace=True)
    df.reset_index(drop=True, inplace=True)
    print(df)
    

    输出:

      Name  start  stop
    0    B    124   200
    1    C    159   200
    2    D     12    24
    3    D     26    30
    4    E    110   160
    

    Repl.it 上的演示。

    【讨论】:

      【解决方案2】:

      对于任何有兴趣的人,我想出了一个方法......

      df3=[]
      for index1, row1 in df1.iterrows():
          for index2, row2 in df2.iterrows():
              if row1["Name"] == row2["Name"]:
                  x = range(row1["start"],row1["stop"])
                  x = set(x)
                  y = range(row2["start"],row2["stop"])
                  if len(x.intersection(y)) > 0:
                      df3.append(row1)
      df3 = pd.DataFrame(df3).reset_index(drop=True)
      print(df3)
      
        Name  start  stop
      0    B    124   200
      1    C    159   200
      2    D     12    24
      3    D     26    30
      4    E    110   160
      

      虽然有点笨拙,但完成了工作。

      如果有人能建议一种不那么混乱的方式,我会很感兴趣!

      【讨论】:

        猜你喜欢
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 2018-11-12
        • 2021-10-31
        • 2021-12-18
        • 1970-01-01
        • 1970-01-01
        • 2021-05-16
        相关资源
        最近更新 更多