2017-07-06 59 views
2

我有兩個數據框,一個包含一些購買數據,另一個包含每週日曆,例如,Python根據複雜標準從第二個數據框中選擇行

df1: 
purchased_at product_id cost 
01-01-2017 1   £10 
01-01-2017 2   £8 
09-01-2017 1   £10 
18-01-2017 3   £12 

df2: 
week_no week_start week_end 
1  31-12-2016 06-01-2017 
2  07-01-2017 13-01-2017 
3  14-01-2017 20-01-2017 

我要使用的數據來自兩個添加「week_no」列於DF1,這是從DF2基於其中在DF1的「purchased_at」日期落在「week_start」和「week_end」之間選擇在日期DF2,即

df1: 
purchased_at product_id cost week_no 
01-01-2017 1   £10 1 
01-01-2017 2   £8 1 
09-01-2017 1   £10 2 
18-01-2017 3   £12 3 

我已經搜查,但我沒能找到其中的數據正在從使用兩者之間的比較第二數據幀拉到一個例子,我已經無法正確應用我找到的任何示例,例如

df1.loc[(df1['purchased_at'] < df2['week_end']) & 
     (df1['purchased_at'] > df2['week_start']), df2['week_no'] 

是不成功的,與ValueError異常「只能比較相同標記的Series對象」

任何人都可以幫助解決這個問題,或者我打開的建議,如果有更好的方式來實現同樣的結果。

編輯添加的DF1的進一步詳細

DF1全數據幀頭部

purchased_at purchase_id product_id product_name transaction_id account_number cost 
01-01-2017 1   1   A    1    AA001   £10 
01-01-2017 2   2   B    1    AA001   £8 
02-01-2017 3   1   A    2    AA008   £10 
03-01-2017 4   3   C    3    AB040   £12 
... 
09-01-2017 12   1   A    10    AB102   £10 
09-01-2017 13   2   B    11    AB102   £8 
... 
18-01-2017 20   3   C    15    AA001   £12 

所以purchase_id每一行逐步增加,PRODUCT_ID和PRODUCT_NAME有一個1:1的關係, transaction_id也會逐步增加,但是在一個事務中可以有多次購買。

+0

解析'df1'的日期應該足夠了,因爲您在這幾個星期使用的定義似乎是*標準*之一。所以,請看看[this](https://stackoverflow.com/questions/2600775/how-to-get-week-number-in-python),而暫時忘掉'df2'。 –

+0

在您的查詢中,數據框將(通常)具有完全不同的形狀。您需要在每個數據框中構建一個可以[加入]的密鑰(https://pandas.pydata.org/pandas-docs/stable/merging.html)(嘗試在df1中開始每週的某一天)。與上面的評論一起,這應該暗示一個解決方案 – Quickbeam2k1

+0

這不是一個標準的定義,因爲這個數字在未來幾年會持續下去,所以明年將是53-104周等等,這就是爲什麼我想加入它單獨而不是從內置公式計算。 – Sarah

回答

0

如果你的數據框很大,你可以使用這個技巧。

做了充分cartisian產品的所有記錄加入到所有記錄:

df_out = pd.merge(df1.assign(key=1),df2.assign(key=1),on='key') 

下一頁過濾掉不在這種情況下,在purchased_at不week_start之間符合條件的記錄和week_end

(df_out.query('week_start < purchased_at < week_end') 
     .drop(['key','week_start','week_end'], axis=1)) 

輸出:

purchased_at product_id cost week_no 
0 2017-01-01   1 £10  1 
3 2017-01-01   2 £8  1 
7 2017-01-09   1 £10  2 
11 2017-01-18   3 £12  3 

如果您確實有較大的數據框,那麼您可以使用PiRSquared提出的numpy method

a = df1.purchased_at.values 

bh = df2.week_end.values 

bl = df2.week_start.values 

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

pd.DataFrame(
    np.column_stack([df1.values[i], df2.values[j]]), 
    columns=df1.columns.append(df2.columns) 
).drop(['week_start','week_end'],axis=1) 

輸出:

  purchased_at product_id cost week_no 
0 2017-01-01 00:00:00   1 £10  1 
1 2017-01-01 00:00:00   2 £8  1 
2 2017-01-09 00:00:00   1 £10  2 
3 2017-01-18 00:00:00   3 £12  3 
+0

numpy方法看起來非常有用,但是對於'purchase at'日期中的每一個都會複製完整的'week_no'輸出,即,而不是上面的輸出,我有16行: purchased_at product_id ... week_no 2017-01 -01 1 2017年1月1日1 1 2017年1月1日1 2 2017年1月1日1 3 2017年1月1日2 1 2017年1月1日2 1 ... 我代碼看起來符合你的例子,你有什麼想法,哪裏出了問題? – Sarah

+0

道歉,這不是很清楚。總而言之,'purchased_at','product_id'和'cost'行每個被複制四次,week_no輸出爲[1,1,2,3,1,1,2,3,1,1,2, 3,1,1,2,3] – Sarah

+0

Sarah ...您將不得不爲我提供數據和期望的輸出,以便排除故障。我唯一的猜測是,也許我們正在使用我們的連接創建cartisan產品,並且需要在代碼中添加一個額外的約束條件,例如product_id。 –

0

你可以只使用time.strftime()提取之日起的週數。如果你想保持向上計數的幾周內,你需要定義一個「零年」爲您的時間序列的開始,並相應抵消week_no:

import pandas as pd 

data = {'purchased_at': ['01-01-2017', '01-01-2017', '09-01-2017', '18-01-2017'], 'product_id': [1,2,1,3], 'cost':['£10', '£8', '£10', '£12']} 

df = pd.DataFrame(data, columns=['purchased_at', 'product_id', 'cost']) 

def getWeekNo(date, year0): 
    datetime = pd.to_datetime(date, dayfirst=True) 
    year = int(datetime.strftime('%Y')) 
    weekNo = int(datetime.strftime('%U')) 
    return weekNo + 52*(year-year0) 

df['week_no'] = df.purchased_at.apply(lambda x: getWeekNo(x, 2017)) 

在這裏,我用pd.to_dateime()到datestring從轉換df到日期時間對象中。 strftime('%Y')返回一週和strftime('%U')(從第一個星期日開始的一年的第一週。如果星期一應該從星期一開始,改用'%W')。

這樣,您不需要爲一週數量維護一個單獨的DataFrame。

相關問題