Как запросить значения столбцов индекса MultiIndex в pandas

пример кода:

In [171]: A = np.array([1.1, 1.1, 3.3, 3.3, 5.5, 6.6])

In [172]: B = np.array([111, 222, 222, 333, 333, 777])

In [173]: C = randint(10, 99, 6)

In [174]: df = pd.DataFrame(zip(A, B, C), columns=['A', 'B', 'C'])

In [175]: df.set_index(['A', 'B'], inplace=True)

In [176]: df
Out[176]: 
          C
A   B      
1.1 111  20
    222  31
3.3 222  24
    333  65
5.5 333  22
6.6 777  74 

теперь я хочу получить значения:
1 квартале: в диапазоне [3.3, 6.6] - ожидаемое возвращаемое значение: [3.3, 5.5, 6.6] или [3.3, 3.3, 5.5, 6.6] в случае последнего включительно, и [3.3, 5.5] или [3.3, 3.3, 5.5] если нет.
Q2: в диапазоне [2.0, 4.0] - ожидаемое возвращаемое значение: [3.3] или [3.3, 3.3]

то же самое для любого другого MultiIndex измерение, например значения B:
Q3: в диапазоне [111, 500] с повторениями, как количество строк данных в диапазоне - ожидаемое возвращаемое значение: [111, 222, 222, 333, 333]

более формальной:

предположим, что T-таблица со столбцами A, B и C. таблица включает n строк. Ячейки таблицы-это числа, например двойные, B и C целые числа. Давайте создадим таблицы данных таблицы T, назовем ее DF. Зададим столбцы A и B индексов DF (без дублирования, т. е. без отдельных столбцов A и B как индексы, и отдельные данные), т. е. A и B в этом случае MultiIndex.

вопросы:

  1. Как написать запрос на индекс, например, для запроса индекса A (или B), скажем, в интервале меток [120.0, 540.0]? Метки 120.0 и 540.0 существует. Я должен уточнить, что меня интересует только список индексов в качестве ответа на запрос!
  2. как то же самое, но в случае метки 120.0 и 540.0 не существует, но есть этикетки по значению ниже 120, выше 120 и меньше 540 или выше 540?
  3. в случае, если ответ для Q1 и Q2 был уникальными значениями индекса, теперь одинаковыми, но с повторениями, как количество строк данных в диапазоне индексов.

Я знаю ответы на вышеуказанные вопросы в случае столбцов, которые не являются индексами, но в случае индексов, после долгих исследований в интернете и экспериментов с функциональностью панды, Я не преуспевать. Единственный метод (без дополнительного программирования), который я вижу сейчас, - это дублирование A и B в качестве столбцов данных в дополнение к индексу.

3 ответов


запрос df на MultiIndex значения, например where (A > 1.7) и (B :

In [536]: result_df = df.loc[(df.index.get_level_values('A') > 1.7) & (df.index.get_level_values('B') < 666)]

In [537]: result_df
Out[537]: 
          C
A   B      
3.3 222  43
    333  59
5.5 333  56

Следовательно, чтобы получить, например, 'A' значения Индекса, если еще требуется:

In [538]: result_df.index.get_level_values('A')
Out[538]: Index([3.3, 3.3, 5.5], dtype=object)

проблема в том, что в больших данных периодов работы по индексу выбор хуже на 10%, чем сортировка обычных строк. И в повторяющейся работе, в петле накапливалась задержка. Видеть пример:

In [558]: df = store.select(STORE_EXTENT_BURSTS_DF_KEY)

In [559]: len(df)
Out[559]: 12857

In [560]: df.sort(inplace=True)

In [561]: df_without_index = df.reset_index()

In [562]: %timeit df.loc[(df.index.get_level_values('END_TIME') > 358200) & (df.index.get_level_values('START_TIME') < 361680)]
1000 loops, best of 3: 562 µs per loop

In [563]: %timeit df_without_index[(df_without_index.END_TIME > 358200) & (df_without_index.START_TIME < 361680)]
1000 loops, best of 3: 507 µs per loop

для лучшей читаемости, мы можем просто использовать the query() метод, чтобы избежать длительных df.index.get_level_values() и reset_index/set_index туда и обратно.

вот цель DataFrame:

In [12]: df                                                                    
Out[12]:                                                                       
          C                                                                    
A   B                                                                          
1.1 111  68                                                                    
    222  40                                                                    
3.3 222  20                                                                    
    333  11                                                                    
5.5 333  80                                                                    
6.6 777  51 

ответ 1 квартале (A в границах [3.3, 6.6]):

In [13]: df.query('3.3 <= A <= 6.6') # for closed interval                       
Out[13]:                                                                       
          C                                                                    
A   B                                                                          
3.3 222  20                                                                    
    333  11                                                                    
5.5 333  80                                                                    
6.6 777  51                                                                    

In [14]: df.query('3.3 < A < 6.6') # for open interval                         
Out[14]:                                                                       
          C                                                                    
A   B                                                                          
5.5 333  80

и конечно можно поиграть с <, <=, >, >= для любого вида включение.


аналогично, ответ для Q2 (A в границах [2.0, 4.0]):

In [15]: df.query('2.0 <= A <= 4.0')                                        
Out[15]:                                                                    
          C                                                                 
A   B                                                                       
3.3 222  20                                                                 
    333  11 

ответ Q3 (B в границах [111, 500]):

In [16]: df.query('111 <= B <= 500')                                        
Out[16]:                                                                    
          C                                                                 
A   B                                                                       
1.1 111  68                                                                 
    222  40                                                                 
3.3 222  20                                                                 
    333  11                                                                 
5.5 333  80

и более того, вы можете комбината запрос для col A и B очень естественно!

In [17]: df.query('0 < A < 4 and 150 < B < 400')                            
Out[17]:                                                                    
          C                                                                 
A   B                                                                       
1.1 222  40                                                                 
3.3 222  20                                                                 
    333  11

С "float", как индекс, вы всегда хотите использовать его как столбец, а не прямое действие индексирования. Все они будут работать независимо от того, существуют конечные точки или нет.

In [11]: df
Out[11]: 
          C
A   B      
1.1 111  81
    222  45
3.3 222  98
    333  13
5.5 333  89
6.6 777  98

In [12]: x = df.reset_index()

1 квартале

In [13]: x.loc[(x.A>=3.3)&(x.A<=6.6)]
Out[13]: 
     A    B   C
2  3.3  222  98
3  3.3  333  13
4  5.5  333  89
5  6.6  777  98

Q2

In [14]: x.loc[(x.A>=2.0)&(x.A<=4.0)]
Out[14]: 
     A    B   C
2  3.3  222  98
3  3.3  333  13

Q3

In [15]: x.loc[(x.B>=111.0)&(x.B<=500.0)]
Out[15]: 
     A    B   C
0  1.1  111  81
1  1.1  222  45
2  3.3  222  98
3  3.3  333  13
4  5.5  333  89

если вы хотите вернуть индексы, просто установите их. Это дешевая операция.

In [16]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B'])
Out[16]: 
          C
A   B      
1.1 111  81
    222  45
3.3 222  98
    333  13
5.5 333  89

если вы действительно хотите фактические значения Индекса

In [5]: x.loc[(x.B>=111.0)&(x.B<=500.0)].set_index(['A','B']).index
Out[5]: 
MultiIndex
[(1.1, 111), (1.1, 222), (3.3, 222), (3.3, 333), (5.5, 333)]