提取series中的數值_Series取反不簡單
技術標籤:提取series中的數值
Series取反·不簡單
此篇介紹今天遇到的一個有意思的bug
與bug不期而遇
今天coding完畢,測試程式碼的時候,下面一段程式碼出現了錯誤。
amp_samples = value[value].index.tolist()not_amp_samples=value[~value].index.tolist()
value
是一個值為True
或False
的Series
,我想從中分別獲取值為True
和False
的index
列表。
程式碼很簡單,pandas
中使用~
進行條件取反。於是,利用value[value]
和value[~value]
但是出錯了。
IndexError Traceback (most recent call last)-38-e29c78a5ffe7> ----> 1 value[~value]IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
index
錯誤。而且很清楚的寫明是value[~value]
而不是value[value]
復現
第一反應就是~
條件取反是不是在Series
中不能用。
進入ipython進行測試。
import pandas as pdseries = pd.Series({'R1':True,'R2':False,'R3':False,'R4':True})print(series[series])print(series[~series])
out:
R1 TrueR4 Truedtype: boolR2 FalseR3 Falsedtype: bool
是正常的。
那麼第二反應就是,前期的處理導致value
出現除了True
和False
的其他值。取正時非bool
值會自動轉換,但是條件取反就會失敗。
於是,在程式碼中加print
獲取value
的值。
R1 TrueR2 FalseR3 FalseR4 Truedtype: object
看起來很正常啊。
思考
A FEW MINUTES LATER。
我開始覺得是伺服器上的pandas
和本地版本不同導致的。於是在伺服器上寫code,無法復現。
15 MINUTES LATER。
終於,我發現,有那麼一絲絲不同。
不知道各位注意到沒有,程式碼輸出中的Value
最後的dtype
是object
,而我的測試程式碼中的dtype
是bool
。我們知道,object
是pandas
中的通用型別,雖然它通常用來儲存字串,但也可以儲存列表、元組等元素。
print(pd.Series({'R1':'sssimon yang'}))print(pd.Series({'R1':(1,2,3)}))print(pd.Series({'R1':[1,2,3]}))
out:
R1 sssimon yangdtype: objectR1 (1, 2, 3)dtype: objectR1 [1, 2, 3]dtype: object
雖然目前不知道object
與bool
會不會影響條件取反,但是為什麼程式碼中的value
是object
型別呢?
錯誤之前的程式碼是這樣的。
df['count'] = np.sum(patient_cnv[samples_map.values()],axis=1) #samples_map.values()的結果類似['R1','R2','R3','R4']for index, value in df.iterrows(): value = value[list(samples_map.values())]
可以看出,之前是想算出所有正值的總count
,然後遍歷每一行。
用示例看一下加一個數值會不會影響型別。
series = pd.Series({'R1':True,'R2':False,'R3':False,'R4':True})print(series)series['count'] = series.sum()print(series)
out:
R1 TrueR2 FalseR3 FalseR4 Truedtype: boolR1 1R2 0R3 0R4 1count 2dtype: int64
嘖,直接從bool
變為了int
。那還是用dataframe
模擬吧。
samples = ['R1','R2','R3','R4']df = pd.DataFrame([[True,False,False,True]],columns=samples)print(df)print(df.iloc[0])df['count'] = df[samples].sum(axis=1)print(df)print(df.iloc[0])
out:
R1 R2 R3 R40 True False False TrueR1 TrueR2 FalseR3 FalseR4 TrueName: 0, dtype: bool R1 R2 R3 R4 count0 True False False True 2R1 TrueR2 FalseR3 FalseR4 Truecount 2Name: 0, dtype: object
確實是從bool
變為了object
,因為各列很有可能是不同型別的值,所以內部機制可能是在取行時傾向於使用object
。
那趕緊取反一下試試。
series = df.iloc[0]series = series[samples]print(series)print(series[series])print(series[~series])
out:
R1 TrueR2 FalseR3 FalseR4 TrueName: 0, dtype: objectR1 TrueR4 TrueName: 0, dtype: objectR2 FalseR3 FalseName: 0, dtype: object
咦,沒有錯誤,不應該啊。
無限逼近
再看原來的程式碼,裡面有個iterrows
,復現的不夠像?
for index, series in df.iterrows(): series = series[samples] print(series) print(series[series]) print(series[~series])
out:
R1 TrueR2 FalseR3 FalseR4 TrueName: 0, dtype: objectR1 TrueR4 TrueName: 0, dtype: object---------------------------------------------------------------------------IndexError Traceback (most recent call last)-96-ba90254b282a> 3 print(series) 4 print(series[series])----> 5 print(series[~series])
絕了,iterrows
和iloc
取出的行型別都是object
,一個可以條件取反,一個不能?
知識盲區。
不過既然復現了,那就要看看這神奇的取反結果是什麼。
index,series = list(df.iterrows())[0]series = series[samples]print(series)print(~series)
out:
R1 TrueR2 FalseR3 FalseR4 TrueName: 0, dtype: objectR1 -2R2 -1R3 -1R4 -2Name: 0, dtype: object
-2
,-1
,那看來是以True
為1
,False
為0
進行的按位取反。
再看iloc
中的。
series = df.iloc[0]series = series[samples]print(series)print(~series)
out:
R1 TrueR2 FalseR3 FalseR4 TrueName: 0, dtype: objectR1 FalseR2 TrueR3 TrueR4 FalseName: 0, dtype: object
正常的條件取反。
分析
我們知道在raw python中存在按位取反~1
輸出-2
,所以這種結果看起來是raw python與pandas
在~
使用上的衝突,在iloc
結果中還可以保持pandas
中的條件取反,但是在iterrows()
中,按位取反就被暴露出來了。
按照道理來講這兩個之前不應該有區別,所以我認為這是個pandas
中的bug,看能不能提個tissue。
解決起來當然就很簡單了,強制轉為bool型別就可以了。
value = value.astype(bool)
我
我是SSSimon Yang,關注我,用code解讀世界