Подтвердить что ты не робот

Как запросить значения столбцов индекса 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 

Теперь я хочу получить значения A:
Q1: в диапазоне [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 строк. Ячейками таблицы являются числа, например A double, B и C целые числа. Позвольте создать DataFrame таблицы T, назовем его DF. Пусть заданы столбцы A и B индексов DF (без дублирования, т.е. Нет отдельных столбцов A и B в качестве индексов и отдельно как данные), то есть A и B в этом случае MultiIndex.

Вопросы:

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

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

4b9b3361

Ответ 1

Чтобы запросить df значениями MultiIndex, например, где (A > 1.7) и (B < 666):

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

Ответ 2

Для лучшей читаемости мы можем просто использовать метод 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 

Ответ для Q1 (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 

Ответ на вопрос 3 (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

И более того, вы можете комбинировать запрос для столбцов 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

Ответ 3

С индексом "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()

Q 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)]