2017-06-05 67 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語法非常易讀。