簡體   English   中英

從 pandas MultiIndex 中選擇列

[英]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 然后重新索引(我的實際代碼甚至更混亂,因為獲取列列表不是那么簡單)。 我很確定必須有一些ixxs方法可以做到這一點,但我嘗試的一切都導致了錯誤。

最直接的方法是使用.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時,我們可以像往常一樣使用:來分割幀。

來源: MultiIndex / Advanced Indexing如何使用slice(None)

這不是很好,但也許:

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

會工作?

您可以使用locix我將展示一個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]指的是水平。

ixselect已棄用!

pd.IndexSlice的使用使loc成為比ixselect更可取的選項。


DataFrame.locpd.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"]

希望這會對某人有所幫助

在選擇之前重命名列

  • 樣品 dataframe
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']

一種選擇是使用來自pyjanitorselect_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM