2017-06-05 56 views
8

我经常使用熊猫进行合并(连接),使用范围条件。以大熊猫的范围加入/合并的最佳方式

例如,如果有2个dataframes:

(A_ID,A_value)

(B_ID,B_low,B_high,B_name)

其是大和大约的相同的尺寸(比方说每个2M记录)。

我想在A和B之间进行内连接,所以A_value将在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<=B.B_high)]掩膜应用上的每个值在B中的搜索功能,但它听起来效率低下以及和可能需要的索引优化。

是否有更优雅和/或有效的方式来执行此操作?

+1

[This Q&A](https://stackoverflow.com/questions/15581829/how-to-perform-an-inner-or-outer-join-of-dataframes-with-pandas-on-non-simplisti)可能是相关的。 –

+0

看起来像他们使用类似的方法,我建议我自己(虚拟列,笛卡尔产品和面具过滤器)。令人惊讶的是没有内置的解决方案。 – Dimgold

+1

您是否也看过接受的答案...?不要从Stack Overflow的_questions_中学习。虽然这可能是因为我没有意识到答案是一样的,在这种情况下对不起:) –

回答

4

设置
考虑dataframes AB

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
✌easiest✌的方法是使用numpy次​​广播。
我们寻找大于或等于B_lowA_value的每个实例,而同时A_value小于或等于B_high

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 

+0

惊人的解决方案..我们可以说这是一个交叉连接...如果我想只保留所有行的'A'(基本上是在'A'上留下连接),那么我需要做些什么改变? –

+0

我想减少发生的行的爆裂。有什么想法吗? –

0

不知道那是更有效的,但是你可以直接使用SQL(从模块的sqlite3例如)与熊猫(从this question启发),如:

conn = sqlite3.connect(":memory") 
df2 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"]) 
df1 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"]) 
df1.to_sql("df1", conn, index=False) 
df2.to_sql("df2", conn, index=False) 
qry = "SELECT * FROM df1, df2 WHERE df1.col1 > 0 and df1.col1<0.5" 
tt = pd.read_sql_query(qry,conn) 

根据需要可以调整查询您的应用

0

让我们简单示例:

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

返回

A 
0 2 
1 3 
2 4 
3 5 
4 6 

现在允许在

B_low B_high 
0 1  2 
1 6  8 
2 2  4 
3 3  6 
4 5  6 

在这里我们去界定第二数据帧

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

结果;我们希望输出为指数3和值5

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

结果

A 
3 5.0 
+0

这不是一个联接,只是堆叠 – Dimgold

0

想想看,你的一个数据帧是

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']) 

使用下面你会得到d esired输出

A = A[(A['A_value']>=B['B_low'])&(A['A_value']<=B['B_high'])] 
+0

但是A和b不需要相同的形状和顺序。假设A是3M记录,B是500K。 – Dimgold

1

我不知道它是多么有效,但有人写了一个包装,让您使用SQL语法与大熊猫的对象。这就是所谓的pandasql。该文档明确指出,支持连接。这可能至少比较容易阅读,因为SQL语法非常易读。