Pandas: отрезать мультииндекс по диапазону вторичного индекса
У меня есть серия с MultiIndex, как это:
import numpy as np
import pandas as pd
buckets = np.repeat(['a','b','c'], [3,5,1])
sequence = [0,1,5,0,1,2,4,50,0]
s = pd.Series(
np.random.randn(len(sequence)),
index=pd.MultiIndex.from_tuples(zip(buckets, sequence))
)
# In [6]: s
# Out[6]:
# a 0 -1.106047
# 1 1.665214
# 5 0.279190
# b 0 0.326364
# 1 0.900439
# 2 -0.653940
# 4 0.082270
# 50 -0.255482
# c 0 -0.091730
Я хотел бы получить значения s ['b'], где второй индекс ('sequence
') находится между 2 и 10.
Нарезка по первому индексу отлично работает:
s['a':'b']
# Out[109]:
# bucket value
# a 0 1.828176
# 1 0.160496
# 5 0.401985
# b 0 -1.514268
# 1 -0.973915
# 2 1.285553
# 4 -0.194625
# 5 -0.144112
Но не во втором, по крайней мере, по тому, что кажется двумя наиболее очевидными способами:
1) Это возвращает элементы с 1 по 4, не имеющие ничего общего с значениями индекса
s['b'][1:10]
# In [61]: s['b'][1:10]
# Out[61]:
# 1 0.900439
# 2 -0.653940
# 4 0.082270
# 50 -0.255482
Однако, если я отменяю индекс, а первый индекс является целым, а второй - строкой, он работает:
In [26]: s
Out[26]:
0 a -0.126299
1 a 1.810928
5 a 0.571873
0 b -0.116108
1 b -0.712184
2 b -1.771264
4 b 0.148961
50 b 0.089683
0 c -0.582578
In [25]: s[0]['a':'b']
Out[25]:
a -0.126299
b -0.116108
Ответы
Ответ 1
Как Ответы Робби-Кларкена, с 0,14 вы можете передать фрагмент в кортеже, который вы проходите для поиска:
In [11]: s.loc[('b', slice(2, 10))]
Out[11]:
b 2 -0.65394
4 0.08227
dtype: float64
В самом деле, вы можете передать срез для каждого уровня:
In [12]: s.loc[(slice('a', 'b'), slice(2, 10))]
Out[12]:
a 5 0.27919
b 2 -0.65394
4 0.08227
dtype: float64
Примечание: срез включен.
Старый ответ:
Вы также можете сделать это, используя:
s.ix[1:10, "b"]
(Это хорошая практика делать в одном ix/loc/iloc, поскольку эта версия позволяет назначать.)
Этот ответ был написан до введения iloc в начале 2013 года, то есть местоположение/целое местоположение, которое может быть предпочтительным в этом случае. Причина, по которой она была создана, заключалась в том, чтобы удалить неоднозначность из объектов с индексом pandas с целыми индексами и описать более подробно: "Я нарезаю позицию".
s["b"].iloc[1:10]
Тем не менее, я не согласен с документами, что ix:
наиболее надежный и последовательный способ
это не так, самый последовательный способ - описать, что вы делаете:
- использовать loc для ярлыков
- использовать iloc для позиции
- используйте ix для обоих (если вам действительно нужно)
Помните zen python:
явный лучше, чем неявный
Ответ 2
Начиная с панд 0.14.0, можно разделить многоиндексированные объекты, предоставив .loc
кортеж, содержащий объекты фрагментов:
In [2]: s.loc[('b', slice(2, 10))]
Out[2]:
b 2 -1.206052
4 -0.735682
dtype: float64
Ответ 3
Лучшим способом, который я могу придумать, является использование "select" в этом случае. Хотя он даже говорит в документах, что "этот метод следует использовать только тогда, когда нет более прямого пути".
Индексирование и выбор данных
In [116]: s
Out[116]:
a 0 1.724372
1 0.305923
5 1.780811
b 0 -0.556650
1 0.207783
4 -0.177901
50 0.289365
0 1.168115
In [117]: s.select(lambda x: x[0] == 'b' and 2 <= x[1] <= 10)
Out[117]: b 4 -0.177901
Ответ 4
Начиная с панды 0.15.0 это работает:
s.loc['b', 2:10]
Выход:
b 2 -0.503023
4 0.704880
dtype: float64
С DataFrame
это немного отличается (источник):
df.loc(axis=0)['b', 2:10]
Ответ 5
не уверен, что это идеально, но работает, создавая маску
In [59]: s.index
Out[59]:
MultiIndex
[('a', 0) ('a', 1) ('a', 5) ('b', 0) ('b', 1) ('b', 2) ('b', 4)
('b', 50) ('c', 0)]
In [77]: s[(tpl for tpl in s.index if 2<=tpl[1]<=10 and tpl[0]=='b')]
Out[77]:
b 2 -0.586568
4 1.559988
EDIT: Решение hayden - это способ пойти