Как я могу создать объект среза DataFrame по частям?
У меня есть DataFrame, и я хочу выбрать из него определенные строки и столбцы. Я знаю, как это сделать, используя loc
. Тем не менее, я хочу иметь возможность указывать каждый критерий индивидуально, а не за один раз.
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]]
Ответы
Ответ 1
Настроить на ответ Теда Петроу:
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[...]
) с аргументом 'dynamic'.
Вы также можете избежать создания объектов 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)]
Ответ 2
Вы можете добиться этого, используя встроенную функцию slice
. Вы не можете создавать срезы со строками, поскольку ':' является литералом, а не синтаксическим.
slice_1 = 'foo'
slice_2 = slice(None)
column_slice = slice('A', 'B')
df.loc[idx[slice_1, slice_2], idx[column_slice]]
Ответ 3
Возможно, вам придется создавать свои "списки срезов" немного иначе, чем вы предполагали, но здесь относительно компактный метод с использованием 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
Этот метод также требует, чтобы вы были явно о своем втором индексе и столбцах, к сожалению. Но компьютеры отлично справляются с длинными утомительными списками для вас, если вы спросите красиво.
EDIT - пример метода динамического построения списка срезов, который можно использовать, как указано выше.
Здесь функция, которая берет блок данных и выплескивает список, который затем может быть использован для создания блока данных запроса, чтобы обрезать оригинал. Он работает только с кадрами данных с 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
Ответ 4
Вы имеете в виду это?
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
Ответ 5
Вы просто это имели в виду?
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]
к вашему текущему разрезаемому виду.