2017-10-07 49 views
4

我有一個多指標數據幀是這樣的:如何將多索引數據幀的子集存儲在新的數據框中?

import pandas as pd 
import numpy as np 


df = pd.DataFrame({'ind1': list('aaaaaaaaabbbbbbbbb'), 
        'ind2': list('cccdddeeecccdddeee'), 
        'ind3': list(range(3))*6, 
        'val1': list(range(100, 118)), 
        'val2': list(range(70, 88))}) 

df_mult = df.set_index(['ind1', 'ind2', 'ind3']) 

       val1 val2 
ind1 ind2 ind3    
a c 0  100 70 
      1  101 71 
      2  102 72 
    d 0  103 73 
      1  104 74 
      2  105 75 
    e 0  106 76 
      1  107 77 
      2  108 78 
b c 0  109 79 
      1  110 80 
      2  111 81 
    d 0  112 82 
      1  113 83 
      2  114 84 
    e 0  115 85 
      1  116 86 
      2  117 87 

我現在可以使用.loc這樣

df_subs = df_mult.loc['a', ['c', 'd'], :] 

現在給人的預期

   val1 val2 
ind1 ind2 ind3    
a c 0  100 70 
      1  101 71 
      2  102 72 
    d 0  103 73 
      1  104 74 
      2  105 75 

如果我選擇它的一個子集想要再次選擇df_subs的子集,例如

df_subs.loc['a', 'c', :] 

作品並給出

 val1 val2 
ind3    
0  100 70 
1  101 71 
2  102 72 

然而

df_subs.loc[:, 'c', :] 

失敗,並給出了一個錯誤

KeyError: 'the label [c] is not in the [columns]'

爲什麼會失敗?

編輯

本來,我在這個職位上工作兩個問題。我把它分成兩部分,第二個問題可以找到here

+0

https://pandas.pydata.org/pandas-docs/stable/advanced.html爲什麼不使用'slice'或'IndexSlice' – Wen

+0

@Wen:當然,可能會有更好的選擇,但我仍然想了解上述嘗試失敗的原因。如果這些嘗試完全錯誤,那麼很高興看到正確的做法,即可行的替代方案。 – Cleb

+0

@JohnE:好的,這已經很好了。我仍然需要弄清楚什麼時候我不得不使用'IndexSlice'。但似乎它沒有改變問題1的任何內容:'df_mult.loc [pd.IndexSlice ['a',['c','d'],:],:]。index'仍然顯示出'a'和' b'在0級;任何想法爲什麼? – Cleb

回答

2

通過使用IndexSlice

idx = pd.IndexSlice 
df_subs.loc[idx[:, 'c',:],:] 
Out[159]: 
       val1 val2 
ind1 ind2 ind3    
a c 0  100 70 
      1  101 71 
      2  102 72 

,或者需要特定的片上的行或列

df_subs.loc(axis=0)[:, 'c', :] 
Out[196]: 
       val1 val2 
ind1 ind2 ind3    
a c 0  100 70 
      1  101 71 
      2  102 72 

之所以.loc[:, 'c', :]不能工作:

您應指定所有.loc說明符中的軸,表示索引和列的索引器。有一些不明確的情況,其中通過的索引器可能被錯誤地解釋爲索引兩個軸,而不是針對行的MuliIndex。

Link1

Link2

+0

好吧,很高興知道一個可行的替代方案(upvoted)。但是,你知道爲什麼'.loc'版本不工作嗎?同樣''df_subs.loc [idx [:,'c',:],:]。index'在0級仍然有'a'和'b';任何想法爲什麼? – Cleb

+0

@Cleb這是一個討論git。你可以檢查我提供的兩個鏈接〜:-)是更安全的,你可以只使用slice或IndexSlice :) – Wen

+0

有趣的閱讀,謝謝。我還編輯了關於問題1的問題;對此有何評論? (現在我不確定這是否確實是相關的,所以我應該把它分解成兩個我認爲的問題)。 – Cleb

1

顯然,使用.loc可能持續的指數在其原來的形式,直到它們被重置。使用.copy()來避免原始數據幀的任何視圖仍然存在多索引值。

df_subs = df_mult.loc['a', ['c', 'd'], :].copy() 

print(df_subs.index) 
# MultiIndex(levels=[['a', 'b'], ['c', 'd', 'e'], [0, 1, 2]], 
#   labels=[[0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]], 
#   names=['ind1', 'ind2', 'ind3']) 

此外,通過值濾波仍然保留multindex值:

df_subs = df_mult[df_mult['val1'] <= 105] 

print(df_subs) 
#     val1 val2 
# ind1 ind2 ind3    
# a c 0  100 70 
#   1  101 71 
#   2  102 72 
#  d 0  103 73 
#   1  104 74 
#   2  105 75 

print(df_subs.index) 
# MultiIndex(levels=[['a', 'b'], ['c', 'd', 'e'], [0, 1, 2]], 
#   labels=[[0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]], 
#   names=['ind1', 'ind2', 'ind3']) 

因此,考慮手動復位指數,按照你原來的分配

df_subs = df_mult.loc['a', ['c', 'd'], :].reset_index() 

df_subs = df_subs.set_index(['ind1', 'ind2', 'ind3']) 

print(df_subs.index) 
# MultiIndex(levels=[['a'], ['c', 'd'], [0, 1, 2]], 
#   labels=[[0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1], [0, 1, 2, 0, 1, 2]], 
#   names=['ind1', 'ind2', 'ind3']) 

最後,最後.loc分配(# 2),至少提供可能要求的第一個索引:

df_subs2 = df_subs.loc['a', 'c', :] 
#  val1 val2 
# ind3    
# 0  100 70 
# 1  101 71 
# 2  102 72