2017-03-23 92 views
10

我有一個DataFrame,我想從中選擇特定的行和列。我知道如何使用loc來做到這一點。但是,我希望能夠單獨指定每個標準,而不是一次指定。如何一塊一塊地創建一個DataFrame切片對象?

import numpy as np 
import pandas as pd 
idx = pd.IndexSlice 

index = [np.array(['foo', 'foo', 'qux', 'qux']), 
     np.array(['a', 'b', 'a', 'b'])] 
columns = ["A", "B"] 
df = pd.DataFrame(np.random.randn(4, 2), index=index, columns=columns) 
print df 
print df.loc[idx['foo', :], idx['A':'B']] 

       A   B 
foo a 0.676649 -1.638399 
    b -0.417915 0.587260 
qux a 0.294555 -0.573041 
    b 1.592056 0.237868 


       A   B 
foo a -0.470195 -0.455713 
    b 1.750171 -0.409216 

要求

我希望能夠實現的東西,如下面的代碼位,在這裏我指定每個標準逐一相同的結果。我能夠使用slice_list來允許動態行爲也很重要[即無論在slice_list]中是否有兩個,三個或十個不同的標準,語法都應該有效。

slice_1 = 'foo' 
slice_2 = ':' 
slice_list = [slice_1, slice_2] 

column_slice = "'A':'B'" 
print df.loc[idx[slice_list], idx[column_slice]] 
+0

slice_2意味着在第二個索引(例如a,b)或第一個索引(foo,qux)上進行切片? –

+0

只是第二個。 – bluprince13

+0

您可以從這裏啓發,其中eval用於數據框中的多個條件:http://stackoverflow.com/questions/33699886/filtering-dataframes-in-pandas-use-a-list-of-conditions – WNG

回答

1

構建上the answer by Ted Petrou

slices = [('foo', slice(None)), slice('A', 'B')] 
print df.loc[tuple(idx[s] for s in slices)] 

       A   B 
foo a -0.465421 -0.591763 
    b -0.854938 1.221204 

slices = [('foo', slice(None)), 'A'] 
print df.loc[tuple(idx[s] for s in slices)] 

foo a -0.465421 
    b -0.854938 
Name: A, dtype: float64 

slices = [('foo', slice(None))] 
print df.loc[tuple(idx[s] for s in slices)] 

       A   B 
foo a -0.465421 -0.591763 
    b -0.854938 1.221204 

你必須調用__getitem__loc[...])時使用的元組與 '動態' 的說法。

你也可以避免了手工打造slice對象:

def to_selector(s): 
    if isinstance(s, tuple) or isinstance(s, list): 
     return tuple(map(to_selector, s)) 
    ps = [None if len(p) == 0 else p for p in s.split(':')] 
    assert len(ps) > 0 and len(ps) <= 2 
    if len(ps) == 1: 
     assert ps[0] is not None 
     return ps[0] 
    return slice(*ps) 

query = [('foo', ':'), 'A:B'] 
df.loc[tuple(idx[to_selector(s)] for s in query)] 
9

您可以使用slice內置函數實現這一目標。您不能使用字符串構建切片,因爲':'是文字字符而不是合成字符。

slice_1 = 'foo' 
slice_2 = slice(None) 
column_slice = slice('A', 'B') 
df.loc[idx[slice_1, slice_2], idx[column_slice]] 
+0

謝謝!但是,我希望能夠使用切片列表,而不是指定每個切片。問題已更新! – bluprince13

+0

嗨,你知道如何使用切片列表更有活力,而不是命名單個切片嗎? – bluprince13

4

您可能需要以不同的方式建立自己的「切片列表」一點比你預期的,但這裏的使用df.merge()df.ix[]比較緊湊的方法:

# Build a "query" dataframe 
slice_df = pd.DataFrame(index=[['foo','qux','qux'],['a','a','b']]) 
# Explicitly name columns 
column_slice = ['A','B'] 

slice_df.merge(df, left_index=True, right_index=True, how='inner').ix[:,column_slice] 

Out[]: 
       A   B 
foo a 0.442302 -0.949298 
qux a 0.425645 -0.233174 
    b -0.041416 0.229281 

此方法還需要你明確關於你的第二個索引和專欄,不幸的是。但是,如果你問得很好,電腦很適合爲你製作冗長繁瑣的清單。

編輯 - 動態構建可以像上面一樣使用的切片列表的方法示例。

這裏有一個函數,它接受一個數據幀並且吐出一個列表,然後可以用它來創建一個「查詢」數據框來分割原始數據。它只適用於具有1或2個索引的數據幀。讓我知道如果這是一個問題。

def make_df_slice_list(df): 
    if df.index.nlevels == 1: 
     slice_list = [] 
     # Only one level of index 
     for dex in df.index.unique(): 
      if input("DF index: " + dex + " - Include? Y/N: ") == "Y": 
       # Add to slice list 
       slice_list.append(dex) 
    if df.index.nlevels > 1: 
     slice_list = [[] for _ in xrange(df.index.nlevels)] 
     # Multi level 
     for i in df.index.levels[0]: 
      print "DF index:", i, "has subindexes:", [dex for dex in df.ix[i].index] 
      sublist = input("Enter a the indexes you'd like as a list: ") 
      # if no response, the first entry 
      if len(sublist)==0: 
       sublist = [df.ix[i].index[0]] 
      # Add an entry to the first index list for each sub item passed 
      [slice_list[0].append(i) for item in sublist] 
      # Add each of the second index list items 
      [slice_list[1].append(item) for item in sublist] 
    return slice_list 

我不建議這樣的方式與您的用戶,只需一個例子來溝通。當您使用它時,必須在提示時傳遞字符串(例如"Y""N")以及字符串列表(["a","b"])和空列表[]。例如:

In [115]: slice_list = make_df_slice_list(df) 

DF index: foo has subindexes: ['a', 'b'] 
Enter a the indexes you'd like as a list: [] 
DF index: qux has subindexes: ['a', 'b'] 
Enter a the indexes you'd like as a list: ['a','b'] 

In [116]:slice_list 
Out[116]: [['foo', 'qux', 'qux'], ['a', 'a', 'b']] 

# Back to my original solution, but now passing the list: 
slice_df = pd.DataFrame(index=slice_list) 
column_slice = ['A','B'] 

slice_df.merge(df, left_index=True, right_index=True, how='inner').ix[:,column_slice] 
Out[117]: 
       A   B 
foo a -0.249547 0.056414 
qux a 0.938710 -0.202213 
    b 0.329136 -0.465999 
0

你的意思呢?

import numpy as np 
import pandas as pd 
idx = pd.IndexSlice 

index = [np.array(['foo', 'foo', 'qux', 'qux']), 
     np.array(['a', 'b', 'a', 'b'])] 
columns = ["A", "B"] 
df = pd.DataFrame(np.random.randn(4, 2), index=index, columns=columns) 
print df 

# 
la1 = lambda df: df.loc[idx['foo', :], idx['A':'B']] 
la2 = lambda df: df.loc[idx['qux', :], idx['A':'B']] 
laList = [la1, la2] 

result = map(lambda la: la(df), laList) 
print result[0] 
print result[1] 

       A   B 
foo a 0.162138 -1.382822 
    b -0.822986 -0.403766 
qux a 0.191695 -1.125841 
    b 0.669254 -0.704894 
       A   B 
foo a 0.162138 -1.382822 
    b -0.822986 -0.403766 
       A   B 
qux a 0.191695 -1.125841 
    b 0.669254 -0.704894 
+0

不,不怕。 – bluprince13

0

你簡單地說這個?

df.loc[idx['foo',:], :].loc[idx[:,'a'], :] 

在一個稍微更一般的形式,例如:

def multiindex_partial_row_slice(df, part_idx, criteria): 
    slc = idx[tuple([slice(None) if i != part_idx else criteria 
        for i in range(len(df.index.levels))])] 
    return df.loc[slc, :] 

multiindex_partial_row_slice(df, 1, slice('a','b')) 

同樣可以隨時縮小當前列通過附加.loc[:, columns]您當前切片視圖設置。