2016-04-01 43 views
3

我有這樣一個數據幀:如何使用布爾掩碼來分配給熊貓數據框的層次列?

import pandas as pd 
df = pd.DataFrame({ 
    "time": [1, 2, 1, 2], 
    "site": ['a', 'a', 'b', 'b'], 
    "val1": [11, 12, 21, 22], 
    "val2": [101, 102, 201, 202] 
}) 
df.set_index(['time', 'site'], inplace=True, append=False) 
df = df.unstack("site") 
print df 

    val1  val2  
site a b a b 
time     
1  11 21 101 201 
2  12 22 102 202 

我想改變匹配布爾過濾器中的值。例如:

ix = df.val1 > 20 
print ix 

site  a  b 
time    
1  False True 
2  False True 

自然的嘗試將是df.val1[ix] = 50。這是預期的分配,但給出警告:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead

所以,現在我正在嘗試使用df.loc來實現類似的功能。但我找不到用這種布爾掩碼來使用df.loc的方法。這似乎是因爲我使用了分層列,即如果我只有一組值(val1),我沒有太多麻煩。不幸的是,在docs中,分層列上布爾過濾器的分配並不十分完善。

我試過參考df.loc[:,'val1',ix],但是這給了IndexingError: Too many indexers。我試過df.loc[:,'val1'][ix] = 50,這工作,但給出SettingWithCopyWarning

我可以使用df.val1 = df.val1.where(~ix, other=50),但這看起來不直觀,效率低下且不靈活(例如,不容易擴展爲將10添加到現有值)。

是否有一些其他的索引方法,我應該使用基於布爾值掩碼爲數據框的分層列賦值?

編輯延長問題:

我沒有意識到這將是一個問題,但我確實喜歡基於val1val2列和變化值都在這兩個值過濾套列,如下所示:

ix = (df.val1 > 20) | (df.val2 < 102) 
df.val1[ix] = 50 
df.val2[ix] = 150 

是否有一個簡單的索引方法可以做到這一點? numpy ndarrays很容易,但對於熊貓數據框來說似乎更加棘手。

+0

它會是一個選項,你扁平你的專欄? – MaxU

回答

3

你可以只使用一個列表選擇列

idx = df[['val1']] > 20 

idx 
Out[39]: 
     val1  
site  a  b 
time    
1  False True 
2  False True 

df[idx] = 50 

df 
Out[41]: 
    val1  val2  
site a b a b 
time     
1  11 50 101 201 
2  12 50 102 202 
+0

謝謝,這是我問的一個很好的答案。不幸的是,我忘了提及我還想在val2列中更改相應的條目,例如'ix =(df.val1> 20)| (df.val2> 200); df.val1 [ix] = 50; df.val2 [ix] = 150'。任何想法如何做到這一點?標準numpy ndarrays非常簡單,但在熊貓中似乎更棘手。 –

0

出現此問題時,您首先選擇從數據幀按列名了一系列然後嘗試使用布爾面具和值分配給它。具體來說,具有布爾值掩碼的賦值會在內部轉換爲extracted_data.where(-mask,other = value,inplace = True),並引發SettingWithCopyWarning。

如果熊貓能夠保證這種操作會改變原始數據幀而不是提出這個警告,那將是非常好的。 (順便提一句,如果鏈接操作順序顛倒,df[ix]["val1"] = 500df[ix][["val1", "val2"]] = 500不會發出警告,但無法更新原始數據幀)。在解決問題之前,有幾個解決方法。

(1)受@cncggvg答案的啓發:構建一個指定所有需要更新的元素的索引,而不是將兩個索引操作鏈接在一起。

# create a partial index for the boolean operation 
# note: this specifies the second-level columns it will act on, but not 
# the first level, since that was given unambiguously in the df[col] expression 
ix = (df["val1"] > 20) | (df["val2"] < 102) 
# build an index that specifies both the first and second-level columns 
ix2 = pd.concat({"val1": ix}, axis=1) 
# or, to do the same assignment on multiple first-level columns: 
ix2 = pd.concat({"val1": ix, "val2": ix}, axis=1) 
# do the assignment in one step, with no chaining 
df[ix2] = 50 
# or derive new values from current values 
df[ix2] = df[ix2]+50 

(2)通過避免使用隱式series.where(..., inplace=True)我自己.where(..., inplace=False)

ix = (df["val1"] > 20) | (df["val2"] < 102) 
df["val1"] = df["val1"].where(~ix, other=50) 
df["val2"] = df["val2"].where(~ix, other=50) 

# or to assign both columns at once: 
# note: this should work with df[["val1", "val2"]] = ..., but pandas 0.18 
# doesn't realize that that gives the same set of columns as cols.columns 
cols = df[["val1", "val2"]] 
df[cols.columns] = cols.where(~ix, other=50) 
# or with a calculation: 
df[cols.columns] = cols.where(~ix, other=cols+50) 

這些都是比我想比較煩瑣,所以我可能只是複製我的數據框的相關章節進入numpy數組,然後從那裏開始工作。根據http://penandpants.com/2014/09/05/performance-of-pandas-series-vs-numpy-arrays/,這應該有更好的表現。