2014-05-07 13 views
18

Ayrı bir tabloda depolanan tarih değerlerine göre, Pandalar'daki zaman tabanlı toplamaları hesaplamaya çalışıyorum.Python Pandas'ta koşullu birleştirme nasıl yapılır?

birinci masa table_a üst şuna benzer: Burada

COMPANY_ID DATE   MEASURE 
    1 2010-01-01 00:00:00  10 
    1 2010-01-02 00:00:00  10 
    1 2010-01-03 00:00:00  10 
    1 2010-01-04 00:00:00  10 
    1 2010-01-05 00:00:00  10 

tablo oluşturmak için kodu şöyledir:

table_a = pd.concat(\ 
    [pd.DataFrame({'DATE': pd.date_range("01/01/2010", "12/31/2010", freq="D"),\ 
    'COMPANY_ID': 1 , 'MEASURE': 10}),\ 
    pd.DataFrame({'DATE': pd.date_range("01/01/2010", "12/31/2010", freq="D"),\ 
    'COMPANY_ID': 2 , 'MEASURE': 10})]) 

ikinci bir tablo table_b şuna benzer:

 COMPANY  END_DATE 
     1 2010-03-01 00:00:00 
     1 2010-06-02 00:00:00 
     2 2010-03-01 00:00:00 
     2 2010-06-02 00:00:00 

ve bunun oluşturulacak kodu:

table_b = pd.DataFrame({'END_DATE':pd.to_datetime(['03/01/2010','06/02/2010','03/01/2010','06/02/2010']),\ 
        'COMPANY':(1,1,2,2)}) 

Tablo_'de END_DATE tarihinden önceki her 30 günlük dönem için her bir COMPANY_ID tutarı için ölçü sütununun toplamını elde edebilmek istiyorum. Bu

(Bence) SQL eşdeğeri: herhangi bir yardım

+0

'' end_date', table_b'de her birinin örtüşen pencereleri var; ör. şirket 1, 2010-03-01 ve 2010-03-15 arasındaki bir bitiş_tarihine sahip olabilir. –

+0

Merhaba @KarlD evet potansiyel olarak. – JAB

cevap

25

Eh için

 select 
b.COMPANY_ID, 
b.DATE 
sum(a.MEASURE) AS MEASURE_TO_END_DATE 
from table_a a, table_b b 
where a.COMPANY = b.COMPANY and 
     a.DATE < b.DATE and 
     a.DATE > b.DATE - 30 
group by b.COMPANY; 

sayesinde birkaç yollar geliyor. (1) veri çerçevesini company üzerinde birleştirerek havaya uçurun ve ardından birleştirme işleminden sonraki 30 günlük pencereleri filtreleyin. Bu hızlı olmalı ama çok fazla bellek kullanabilir. (2) 30 günlük pencerede birleştirme ve filtrelemeyi gruplara taşıyın. o yavaş olur ama daha az belleği kullanmak gerektiğinden, bu her grup için bir birleştirme sonuçlanan

Seçenek # 1

(Ben senin örnek verileri genişletilmiş) aşağıdaki gibi veri görünüyor varsayalım:

tabanlı o seçin

windows['beg_date'] = (windows['end_date'].values.astype('datetime64[D]') - 
         np.timedelta64(30,'D')) 
print windows 

    company end_date beg_date 
0  0 2010-02-01 2010-01-02 
1  0 2010-03-15 2010-02-13 
2  1 2010-04-01 2010-03-02 
3  1 2010-05-15 2010-04-15 

Şimdi bir birleştirme yapmak ve:

print df 

    company  date measure 
0   0 2010-01-01  10 
1   0 2010-01-15  10 
2   0 2010-02-01  10 
3   0 2010-02-15  10 
4   0 2010-03-01  10 
5   0 2010-03-15  10 
6   0 2010-04-01  10 
7   1 2010-03-01  5 
8   1 2010-03-15  5 
9   1 2010-04-01  5 
10  1 2010-04-15  5 
11  1 2010-05-01  5 
12  1 2010-05-15  5 

print windows 

    company end_date 
0  0 2010-02-01 
1  0 2010-03-15 
2  1 2010-04-01 
3  1 2010-05-15 

30 günlük pencereler için bir başlangıç ​​tarihini oluştur

df = df.merge(windows,on='company',how='left') 
df = df[(df.date >= df.beg_date) & (df.date <= df.end_date)] 
print df 

    company  date measure end_date beg_date 
2   0 2010-01-15  10 2010-02-01 2010-01-02 
4   0 2010-02-01  10 2010-02-01 2010-01-02 
7   0 2010-02-15  10 2010-03-15 2010-02-13 
9   0 2010-03-01  10 2010-03-15 2010-02-13 
11  0 2010-03-15  10 2010-03-15 2010-02-13 
16  1 2010-03-15  5 2010-04-01 2010-03-02 
18  1 2010-04-01  5 2010-04-01 2010-03-02 
21  1 2010-04-15  5 2010-05-15 2010-04-15 
23  1 2010-05-01  5 2010-05-15 2010-04-15 
25  1 2010-05-15  5 2010-05-15 2010-04-15 

Sen company ve end_date üzerine gruplayarak 30 günlük pencere toplamları hesaplayabiliriz: beg_date ve end_date içinde date düşer eğer n

print df.groupby(['company','end_date']).sum() 

        measure 
company end_date   
0  2010-02-01  20 
     2010-03-15  30 
1  2010-04-01  10 
     2010-05-15  15 

Seçenek 2. Taşı hepsi GroupBy içerisine girmektedir.Bu bellek daha iyi olmalı ama ben çok daha yavaş düşünürdüm:

windows['beg_date'] = (windows['end_date'].values.astype('datetime64[D]') - 
         np.timedelta64(30,'D')) 

def cond_merge(g,windows): 
    g = g.merge(windows,on='company',how='left') 
    g = g[(g.date >= g.beg_date) & (g.date <= g.end_date)] 
    return g.groupby('end_date')['measure'].sum() 

print df.groupby('company').apply(cond_merge,windows) 

company end_date 
0  2010-02-01 20 
     2010-03-15 30 
1  2010-04-01 10 
     2010-05-15 15 

Şimdi pencereleri (örnek veriler gibi) üst üste asla diğer seçenek ise sen gelmez alternatif olarak aşağıdaki gibi bir şey yapabileceğini

windows['date'] = windows['end_date'] 

df = df.merge(windows,on=['company','date'],how='outer') 
print df 

    company  date measure end_date 
0   0 2010-01-01  10  NaT 
1   0 2010-01-15  10  NaT 
2   0 2010-02-01  10 2010-02-01 
3   0 2010-02-15  10  NaT 
4   0 2010-03-01  10  NaT 
5   0 2010-03-15  10 2010-03-15 
6   0 2010-04-01  10  NaT 
7   1 2010-03-01  5  NaT 
8   1 2010-03-15  5  NaT 
9   1 2010-04-01  5 2010-04-01 
10  1 2010-04-15  5  NaT 
11  1 2010-05-01  5  NaT 
12  1 2010-05-15  5 2010-05-15 

Bu birleştirme esasen dataframe içine pencere bitiş tarihlerini yerleştirir ve daha sonra (grup tarafından) bitiş tarihlerini dolgu size kolayca pencereler toplamıdır oluşturmak için bir yapı kazandıracaktır: 't bir dataframe patlatmak ama oldukça hızlıdır :

df['end_date'] = df.groupby('company')['end_date'].apply(lambda x: x.bfill()) 

print df 

    company  date measure end_date 
0   0 2010-01-01  10 2010-02-01 
1   0 2010-01-15  10 2010-02-01 
2   0 2010-02-01  10 2010-02-01 
3   0 2010-02-15  10 2010-03-15 
4   0 2010-03-01  10 2010-03-15 
5   0 2010-03-15  10 2010-03-15 
6   0 2010-04-01  10  NaT 
7   1 2010-03-01  5 2010-04-01 
8   1 2010-03-15  5 2010-04-01 
9   1 2010-04-01  5 2010-04-01 
10  1 2010-04-15  5 2010-05-15 
11  1 2010-05-01  5 2010-05-15 
12  1 2010-05-15  5 2010-05-15 

df = df[df.end_date.notnull()] 
df['beg_date'] = (df['end_date'].values.astype('datetime64[D]') - 
        np.timedelta64(30,'D')) 

print df 

    company  date measure end_date beg_date 
0   0 2010-01-01  10 2010-02-01 2010-01-02 
1   0 2010-01-15  10 2010-02-01 2010-01-02 
2   0 2010-02-01  10 2010-02-01 2010-01-02 
3   0 2010-02-15  10 2010-03-15 2010-02-13 
4   0 2010-03-01  10 2010-03-15 2010-02-13 
5   0 2010-03-15  10 2010-03-15 2010-02-13 
7   1 2010-03-01  5 2010-04-01 2010-03-02 
8   1 2010-03-15  5 2010-04-01 2010-03-02 
9   1 2010-04-01  5 2010-04-01 2010-03-02 
10  1 2010-04-15  5 2010-05-15 2010-04-15 
11  1 2010-05-01  5 2010-05-15 2010-04-15 
12  1 2010-05-15  5 2010-05-15 2010-04-15 

df = df[(df.date >= df.beg_date) & (df.date <= df.end_date)] 
print df.groupby(['company','end_date']).sum() 

        measure 
company end_date   
0  2010-02-01  20 
     2010-03-15  30 
1  2010-04-01  10 
     2010-05-15  15 

Başka bir alternatif, ilk veri çerçevenizi günlük verilere yeniden örneklendirmek ve daha sonra 30 gün pencereli rolling_sums'ları hesaplamak; ve ilgilendiğiniz en son tarihleri ​​seçin. Bu da oldukça yoğun bellek olabilir.

+0

Teşekkür @Karl D bu harika bir cevaptı. Iki stratejiyi ve bunların güçlü/zayıf yönlerini göstermek için – JAB

+0

+1. – ojdo

İlgili konular