[英]Selecting columns from pandas MultiIndex
我有 DataFrame 和 MultiIndex 列,如下所示:
# sample data
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randn(4, 6), columns=col)
data
從第二級僅選擇特定列(例如['a', 'c']
,而不是范圍)的正確、簡單的方法是什么?
目前我是這樣做的:
import itertools
tuples = [i for i in itertools.product(['one', 'two'], ['a', 'c'])]
new_index = pd.MultiIndex.from_tuples(tuples)
print(new_index)
data.reindex_axis(new_index, axis=1)
然而,它感覺不是一個好的解決方案,因為我必須淘汰itertools
,手動構建另一個 MultiIndex 然后重新索引(我的實際代碼甚至更混亂,因為獲取列列表不是那么簡單)。 我很確定必須有一些ix
或xs
方法可以做到這一點,但我嘗試的一切都導致了錯誤。
最直接的方法是使用.loc
:
>>> data.loc[:, (['one', 'two'], ['a', 'b'])]
one two
a b a b
0 0.4 -0.6 -0.7 0.9
1 0.1 0.4 0.5 -0.3
2 0.7 -1.6 0.7 -0.8
3 -0.9 2.6 1.9 0.6
請記住[]
和()
在處理MultiIndex
對象時具有特殊含義:
(...) 元組被解釋為一個多級鍵
(...) 一個列表用於指定多個鍵[在同一級別]
(...) 一個列表元組引用一個級別中的多個值
當我們編寫(['one', 'two'], ['a', 'b'])
時,元組中的第一個列表指定了MultiIndex
第一級中我們想要的所有值。 元組中的第二個列表指定了我們想要的MultiIndex
第二級的所有值。
編輯 1:另一種可能性是使用slice(None)
來指定我們想要第一級的任何東西(類似於在列表中使用:
進行切片)。 然后指定我們想要的第二級中的哪些列。
>>> data.loc[:, (slice(None), ["a", "b"])]
one two
a b a b
0 0.4 -0.6 -0.7 0.9
1 0.1 0.4 0.5 -0.3
2 0.7 -1.6 0.7 -0.8
3 -0.9 2.6 1.9 0.6
如果語法slice(None)
確實對您有吸引力,那么另一種可能性是使用pd.IndexSlice
,它有助於使用更精細的索引對幀進行切片。
>>> data.loc[:, pd.IndexSlice[:, ["a", "b"]]]
one two
a b a b
0 0.4 -0.6 -0.7 0.9
1 0.1 0.4 0.5 -0.3
2 0.7 -1.6 0.7 -0.8
3 -0.9 2.6 1.9 0.6
當使用pd.IndexSlice
時,我們可以像往常一樣使用:
來分割幀。
這不是很好,但也許:
>>> data
one two
a b c a b c
0 -0.927134 -1.204302 0.711426 0.854065 -0.608661 1.140052
1 -0.690745 0.517359 -0.631856 0.178464 -0.312543 -0.418541
2 1.086432 0.194193 0.808235 -0.418109 1.055057 1.886883
3 -0.373822 -0.012812 1.329105 1.774723 -2.229428 -0.617690
>>> data.loc[:,data.columns.get_level_values(1).isin({"a", "c"})]
one two
a c a c
0 -0.927134 0.711426 0.854065 1.140052
1 -0.690745 -0.631856 0.178464 -0.418541
2 1.086432 0.808235 -0.418109 1.886883
3 -0.373822 1.329105 1.774723 -0.617690
會工作?
您可以使用loc
或ix
我將展示一個loc
示例:
data.loc[:, [('one', 'a'), ('one', 'c'), ('two', 'a'), ('two', 'c')]]
當您有一個 MultiIndexed DataFrame,並且您只想過濾掉一些列時,您必須傳遞與這些列匹配的元組列表。 所以 itertools 方法非常好,但您不必創建新的 MultiIndex:
data.loc[:, list(itertools.product(['one', 'two'], ['a', 'c']))]
我認為有一個更好的方法(現在),這就是為什么我費心把這個問題(這是谷歌的最高結果)從陰影中拉出來:
data.select(lambda x: x[1] in ['a', 'b'], axis=1)
以快速而干凈的單行方式提供您的預期輸出:
one two
a b a b
0 -0.341326 0.374504 0.534559 0.429019
1 0.272518 0.116542 -0.085850 -0.330562
2 1.982431 -0.420668 -0.444052 1.049747
3 0.162984 -0.898307 1.762208 -0.101360
它主要是不言自明的, [1]
指的是水平。
ix
和select
已棄用! pd.IndexSlice
的使用使loc
成為比ix
和select
更可取的選項。
DataFrame.loc
和pd.IndexSlice
# Setup
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame('x', index=range(4), columns=col)
data
one two
a b c a b c
0 x x x x x x
1 x x x x x x
2 x x x x x x
3 x x x x x x
data.loc[:, pd.IndexSlice[:, ['a', 'c']]]
one two
a c a c
0 x x x x
1 x x x x
2 x x x x
3 x x x x
您也可以將axis
參數設置為loc
以明確您從哪個軸索引:
data.loc(axis=1)[pd.IndexSlice[:, ['a', 'c']]]
one two
a c a c
0 x x x x
1 x x x x
2 x x x x
3 x x x x
MultiIndex.get_level_values
調用data.columns.get_level_values
來過濾loc
是另一種選擇:
data.loc[:, data.columns.get_level_values(1).isin(['a', 'c'])]
one two
a c a c
0 x x x x
1 x x x x
2 x x x x
3 x x x x
這自然可以允許在單個級別上過濾任何條件表達式。 這是一個字典過濾的隨機示例:
data.loc[:, data.columns.get_level_values(1) > 'b']
one two
c c
0 x x
1 x x
2 x x
3 x x
可以在 Pandas MultiIndex DataFrame 中的Select rows 中找到有關切片和過濾 MultiIndex 的更多信息。
要在列索引器的第二級選擇所有名為'a'
和'c'
的列,可以使用切片器:
>>> data.loc[:, (slice(None), ('a', 'c'))]
one two
a c a c
0 -0.983172 -2.495022 -0.967064 0.124740
1 0.282661 -0.729463 -0.864767 1.716009
2 0.942445 1.276769 -0.595756 -0.973924
3 2.182908 -0.267660 0.281916 -0.587835
在這里,您可以閱讀有關切片機的更多信息。
在我看來,使用 slice 對Marc P.的回答稍微簡單一點:
import pandas as pd
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'], ['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randn(4, 6), columns=col)
data.loc[:, pd.IndexSlice[:, ['a', 'c']]]
one two
a c a c
0 -1.731008 0.718260 -1.088025 -1.489936
1 -0.681189 1.055909 1.825839 0.149438
2 -1.674623 0.769062 1.857317 0.756074
3 0.408313 1.291998 0.833145 -0.471879
從 pandas 0.21 左右開始, 不推薦使用 .select 以支持 .loc 。
如果列索引的級別是任意的,這可能會對您有所幫助:
class DataFrameMultiColumn(pd.DataFrame) :
def loc_multicolumn(self, keys):
depth = lambda L: isinstance(L, list) and max(map(depth, L))+1
result = []
col = self.columns
# if depth of keys is 1, all keys need to be true
if depth(keys) == 1:
for c in col:
# select all columns which contain all keys
if set(keys).issubset(set(c)) :
result.append(c)
# depth of 2 indicates,
# the product of all sublists will be formed
elif depth(keys) == 2 :
keys = list(itertools.product(*keys))
for c in col:
for k in keys :
# select all columns which contain all keys
if set(k).issubset(set(c)) :
result.append(c)
else :
raise ValueError("Depth of the keys list exceeds 2")
# return with .loc command
return self.loc[:,result]
.loc_multicolumn
將返回與調用.loc
相同的結果,但不指定每個鍵的級別。 請注意,這可能是一個問題,因為多個列級別的值相同!
np.random.seed(1)
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randint(0, 10, (4,6)), columns=col)
data_mc = DataFrameMultiColumn(data)
>>> data_mc
one two
a b c a b c
0 5 8 9 5 0 0
1 1 7 6 9 2 4
2 5 2 4 2 4 7
3 7 9 1 7 0 6
列表深度 1 要求列表中的所有元素都適合。
>>> data_mc.loc_multicolumn(['a', 'one'])
one
a
0 5
1 1
2 5
3 7
>>> data_mc.loc_multicolumn(['a', 'b'])
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3]
>>> data_mc.loc_multicolumn(['one','a', 'b'])
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3]
列表深度 2 允許鍵列表的笛卡爾積的所有元素。
>>> data_mc.loc_multicolumn([['a', 'b']])
one two
a b a b
0 5 8 5 0
1 1 7 9 2
2 5 2 2 4
3 7 9 7 0
>>> data_mc.loc_multicolumn([['one'],['a', 'b']])
one
a b
0 5 8
1 1 7
2 5 2
3 7 9
最后:如果組合中的所有元素都適合,則給出list(itertools.product(["one"], ['a', 'b']))
中的所有組合。
使用df.loc(axis="columns")
(或df.loc(axis=1)
僅訪問列並切開:
df.loc(axis="columns")[:, ["a", "c"]]
如果多索引具有布爾值,則較早答案之一中給出的 .loc[:, list of column tuples] 方法將失敗,如下例所示:
col = pd.MultiIndex.from_arrays([[False, False, True, True],
[False, True, False, True]])
data = pd.DataFrame(np.random.randn(4, 4), columns=col)
data.loc[:,[(False, True),(True, False)]]
失敗並出現ValueError: PandasArray must be 1-dimensional.
將此與以下示例進行比較,其中索引值是字符串而不是布爾值:
col = pd.MultiIndex.from_arrays([["False", "False", "True", "True"],
["False", "True", "False", "True"]])
data = pd.DataFrame(np.random.randn(4, 4), columns=col)
data.loc[:,[("False", "True"),("True", "False")]]
這工作正常。
您可以將第一個(布爾)場景轉換為第二個(字符串)場景
data.columns = pd.MultiIndex.from_tuples([(str(i),str(j)) for i,j in data.columns],
names=data.columns.names)
然后使用字符串而不是布爾列索引值訪問( names=data.columns.names
參數是可選的,與本示例無關)。 這個例子有一個兩級的列索引,如果你有更多的級別,相應地調整這個代碼。
獲取布爾多級列索引會出現,例如,如果一個交叉表中的列是由兩個或多個比較產生的。
這里有兩個答案,具體取決於您需要的確切輸出。
如果您想從您的選擇中獲得一個級別的數據框(有時可能非常有用),只需使用:
df.xs('theColumnYouNeed', level=1, axis=1)
如果您想保留多索引表單(類似於 metakermit 的答案):
data.loc[:, data.columns.get_level_values(1) == "columnName"]
希望這會對某人有所幫助
import pandas as pd
import numpy as np
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randn(4, 6), columns=col)
data
data.columns = ['_'.join(x) for x in data.columns]
data
data['one_a']
一種選擇是使用來自pyjanitor的select_columns ,您可以在其中使用字典到 select - 字典選項僅限於 MultiIndex - 字典的鍵是級別(數字或標簽),值是標簽(s) ) 待選:
# pip install pyjanitor
import pandas as pd
import janitor
data.select_columns({1:['a','c']})
one two
a c a c
0 -0.089182 -0.523464 -0.494476 0.281698
1 0.968430 -1.900191 -0.207842 -0.623020
2 0.087030 -0.093328 -0.861414 -0.021726
3 -0.952484 -1.149399 0.035582 0.922857
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.