2014-05-07 14 views
18

Tôi đang cố gắng tính toán tập hợp dựa trên thời gian trong Pandas dựa trên giá trị ngày được lưu trữ trong một bảng riêng biệt.Làm thế nào để thực hiện việc tham gia có điều kiện trong Pandas trăn?

Đỉnh của table_a bảng đầu tiên trông như thế này:

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 

Đây là đoạn mã để tạo ra bảng:

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

Bảng thứ hai, table_b trông như thế này:

 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 

và mã để tạo nó là:

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)}) 

Tôi muốn có thể nhận tổng của cột đo lường cho từng COMPANY_ID cho mỗi khoảng thời gian 30 ngày trước END_DATE trong table_b.

Đây là (tôi nghĩ) SQL tương đương:

 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; 

Nhờ sự giúp đỡ

+0

Hiện tại 'end_date' trong bảng_b đều có cửa sổ chồng chéo; ví dụ: công ty 1 có thể có ngày của năm 2010-03-01 và 2010-03-15. –

+0

Xin chào @KarlD có khả năng. – JAB

Trả lời

25

Vâng, tôi có thể nghĩ ra một vài cách khác nhau. (1) về cơ bản thổi lên các khung dữ liệu bằng cách hợp nhất trên company và sau đó lọc trên các cửa sổ 30 ngày sau khi hợp nhất. Điều này nên được nhanh chóng nhưng có thể sử dụng rất nhiều bộ nhớ. (2) Di chuyển việc hợp nhất và lọc trên cửa sổ 30 ngày thành một nhóm. Điều này dẫn đến một hợp nhất đối với từng nhóm vì vậy nó sẽ chậm hơn nhưng nó phải sử dụng ít bộ nhớ

Lựa chọn # 1

Giả sử dữ liệu của bạn trông giống như sau (tôi mở rộng dữ liệu mẫu của bạn):

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 

Tạo một ngày đầu cho các cửa sổ 30 ngày:

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 

Bây giờ làm một hợp nhất và sau đó chọn dựa o n nếu date thác trong vòng beg_dateend_date:

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 

Bạn có thể tính toán số tiền khoảng thời gian 30 ngày bằng cách nhóm trên companyend_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 

Lựa chọn # 2 Move tất cả sáp nhập vào một groupby.Điều này nên được tốt hơn vào bộ nhớ nhưng tôi sẽ nghĩ chậm hơn nhiều:

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 

Một tùy chọn khác Bây giờ nếu cửa sổ của bạn không bao giờ chồng lên nhau (như trong các dữ liệu chẳng hạn), bạn có thể làm một cái gì đó như sau như một sự thay thế điều đó không 't thổi lên một dataframe nhưng là khá nhanh:

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 

merge này về cơ bản chèn ngày kết thúc cửa sổ của bạn vào dataframe và sau đó san lấp ngày kết thúc (theo cá nhân) sẽ cung cấp cho bạn một cấu trúc dễ dàng tạo ra bạn tổng kết các cửa sổ :

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 

Một cách khác là định lại mẫu dữ liệu đầu tiên của bạn thành dữ liệu hàng ngày và sau đó tính toán rolling_sums với cửa sổ 30 ngày; và chọn ngày cuối cùng mà bạn quan tâm. Điều này cũng có thể là bộ nhớ khá chuyên sâu.

+0

Cảm ơn @Karl D đây là một câu trả lời tuyệt vời. – JAB

+0

+1 để hiển thị hai chiến lược và điểm mạnh/điểm yếu của chúng. – ojdo

Các vấn đề liên quan