Python 在pandas中按范围加入/合并的最佳方式

Python 在pandas中按范围加入/合并的最佳方式,python,pandas,numpy,join,Python,Pandas,Numpy,Join,我经常通过使用范围条件来使用pandas进行合并(join) 例如,如果有2个数据帧: A(A\u id,A\u值) B(B_id,B_low,B_high,B_name) 它们都很大,大小大致相同(比如说每个都有200万条记录) 我想在A和B之间建立一个内部连接,所以A_值应该在B_low和B_high之间 使用以下SQL语法: SELECT * FROM A,B WHERE A_value between B_low and B_high 这将是非常简单、简短和高效的 同时,唯一的方法(没

我经常通过使用范围条件来使用pandas进行合并(join)

例如,如果有2个数据帧:

A(A\u id,A\u值)

B(B_id,B_low,B_high,B_name)

它们都很大,大小大致相同(比如说每个都有200万条记录)

我想在A和B之间建立一个内部连接,所以A_值应该在B_low和B_high之间

使用以下SQL语法:

SELECT *
FROM A,B
WHERE A_value between B_low and B_high
这将是非常简单、简短和高效的

同时,唯一的方法(没有使用我发现的循环)是在两个表中创建一个虚拟列,对其进行连接(相当于交叉连接),然后过滤掉不需要的行。这听起来既沉重又复杂:

A['dummy'] = 1
B['dummy'] = 1
Temp = pd.merge(A,B,on='dummy')
Result = Temp[Temp.A_value.between(Temp.B_low,Temp.B_high)]

我的另一个解决方案是,通过使用
B[(x>=B.B_low)&(x不确定这是否更有效,对每个值应用一个搜索函数,但是您可以直接使用sql(例如,从模块sqlite3)和pandas(灵感来自),如:

连接(“:内存:”) df2=pd.DataFrame(np.random.randn(10,5),列=[“col1”、“col2”、“col3”、“col4”、“col5”]) df1=pd.DataFrame(np.random.randn(10,5),列=[“col1”、“col2”、“col3”、“col4”、“col5”]) df1.to_sql(“df1”,conn,index=False) df2.to_sql(“df2”,conn,index=False)
qry=“从df1、df2中选择*,其中df1.col1>0和df1.col1让我们举一个简单的例子:

df=pd.DataFrame([2,3,4,5,6],columns=['A'])
返回

    A
0   2
1   3
2   4
3   5
4   6
现在让我们定义第二个数据帧

df2=pd.DataFrame([1,6,2,3,5],columns=['B_low'])
df2['B_high']=[2,8,4,6,6]
导致

    B_low   B_high
0   1       2
1   6       8
2   2       4
3   3       6
4   5       6
    A
3   5.0
现在我们开始;我们希望输出是索引3和值5

df.where(df['A']>=df2['B_low']).where(df['A']<df2['B_high']).dropna()

假设您的数据帧是

A = pd.DataFrame([[0,2],[1,3],[2,4],[3,5],[4,6]],columns=['A_id', 'A_value'])
B数据帧是

B = pd.DataFrame([[0,1,2,'a'],[1,4,9,'b'],[2,2,5,'c'],[3,6,7,'d'],[4,8,9,'e']],columns=['B_id', 'B_low', 'B_high', 'B_name'])
使用下面的方法,您将获得所需的输出

A = A[(A['A_value']>=B['B_low'])&(A['A_value']<=B['B_high'])]
A=A[(A['A_值]]>=B['B_低]]和(A['A_值]]设置
考虑DATAFAFRAMS <代码> A < /代码>和<代码> B<代码> < /P>
A = pd.DataFrame(dict(
        A_id=range(10),
        A_value=range(5, 105, 10)
    ))
B = pd.DataFrame(dict(
        B_id=range(5),
        B_low=[0, 30, 30, 46, 84],
        B_high=[10, 40, 50, 54, 84]
    ))

A

   A_id  A_value
0     0        5
1     1       15
2     2       25
3     3       35
4     4       45
5     5       55
6     6       65
7     7       75
8     8       85
9     9       95

B

   B_high  B_id  B_low
0      10     0      0
1      40     1     30
2      50     2     30
3      54     3     46
4      84     4     84

numpy

✌容易的✌方法是使用
numpy
广播。
我们寻找
A_值
大于或等于
B_低
的每个实例,同时
A_值
小于或等于
B_高

a = A.A_value.values
bh = B.B_high.values
bl = B.B_low.values

i, j = np.where((a[:, None] >= bl) & (a[:, None] <= bh))

pd.DataFrame(
    np.column_stack([A.values[i], B.values[j]]),
    columns=A.columns.append(B.columns)
)

   A_id  A_value  B_high  B_id  B_low
0     0        5      10     0      0
1     3       35      40     1     30
2     3       35      50     2     30
3     4       45      50     2     30

我不知道它的效率有多高,但有人编写了一个包装器,允许您对pandas对象使用SQL语法。这就是所谓的。文档明确说明支持联接。这可能至少更容易阅读,因为SQL语法非常可读。

可能相关。看起来他们使用的方法与我建议的方法类似ted我自己(虚拟列、笛卡尔积和掩码过滤器)。令人惊讶的是,没有内置的解决方案。你是否也看到了公认的答案…?永远不要从堆栈溢出的问题中学习。尽管可能是我没有意识到答案做了同样的事情,在这种情况下,对不起:)看起来更像是一个内存优化的解决方案,但一点也不优雅:(谢谢!嗯,是的,
merge
不接受任何函数参数的事实表明,优雅的解决方案可能不可用。但我几乎不知道pandas,所以希望有专家来证明相反的情况:)但是A和b不一定是相同的形状和顺序。假设A是3M记录,b是500K。这不是一个连接,只是堆叠迷宫解决方案。我们可以说这是一个交叉连接…如果我只想保留
A
的所有行(基本上是左连接
A
)那么我需要做什么改变呢?我也想减少正在发生的行数激增。有什么想法吗?这很好,我和Joel有同样的问题,是否可以保留表A的所有值,比如左连接?@piRSquared如果只保留A_id==B_id的行,你会怎么做?我们可以事后再做,但我不这么认为这是最有效的。在我的例子中,我有一个79k行的原始df,它在你的操作之后会变成2.3m行,然后当我只保留A_id==B_id的行时,我就有74k行,这就是我所期望的。这难道不能一次完成吗?对于有可能的行和列的非常大的数据集,这个答案会导致内存溢出=/
a = A.A_value.values
bh = B.B_high.values
bl = B.B_low.values

i, j = np.where((a[:, None] >= bl) & (a[:, None] <= bh))

pd.DataFrame(
    np.column_stack([A.values[i], B.values[j]]),
    columns=A.columns.append(B.columns)
)

   A_id  A_value  B_high  B_id  B_low
0     0        5      10     0      0
1     3       35      40     1     30
2     3       35      50     2     30
3     4       45      50     2     30
pd.DataFrame(
    np.column_stack([A.values[i], B.values[j]]),
    columns=A.columns.append(B.columns)
).append(
    A[~np.in1d(np.arange(len(A)), np.unique(i))],
    ignore_index=True, sort=False
)

    A_id  A_value  B_id  B_low  B_high
0      0        5   0.0    0.0    10.0
1      3       35   1.0   30.0    40.0
2      3       35   2.0   30.0    50.0
3      4       45   2.0   30.0    50.0
4      1       15   NaN    NaN     NaN
5      2       25   NaN    NaN     NaN
6      5       55   NaN    NaN     NaN
7      6       65   NaN    NaN     NaN
8      7       75   NaN    NaN     NaN
9      8       85   NaN    NaN     NaN
10     9       95   NaN    NaN     NaN