панды находят первое появление

Предположим, что у меня есть структурированный dataframe следующим образом:

df = pd.DataFrame({"A":['a','a','a','b','b'],"B":[1]*5})

Столбец A ранее был отсортирован. Я хочу найти индекс первой строки, где df[df.A!='a']. Конечной целью является использование этого индекса для разбиения кадра данных на группы на основе A.

Теперь я понимаю, что есть функциональность groupby. Тем не менее, dataframe довольно большой, и это упрощенный пример игрушек. Поскольку A уже отсортирован, это будет быстрее, если я могу просто найти 1-й индекс, где df.A!='a'. Поэтому важно, чтобы любой метод, который вы используете , останавливается после обнаружения первого элемента.

Ответ 1

idxmax и argmax вернется положение максимального значения или первой позиции, если максимальное значение происходит более одного раза.

используйте idxmax on df.A.ne('a')

df.A.ne('a').idxmax()

3

или эквивалент numpy

(df.A.values != 'a').argmax()

3

Однако, если A уже отсортировано, мы можем использовать searchsorted

df.A.searchsorted('a', side='right')

array([3])

Или эквивалент numpy

df.A.values.searchsorted('a', side='right')

3

Ответ 2

Я обнаружил, что есть функция first_valid_index для Pandas DataFrames, которая будет выполнять эту работу, ее можно использовать следующим образом:

df[df.A!='a'].first_valid_index()

3

Однако эта функция кажется очень медленной. Даже получение первого индекса отфильтрованного фрейма данных происходит быстрее:

df.loc[df.A!='a','A'].index[0]

Ниже я сравниваю общее время (с) повторения вычислений 100 раз для этих двух вариантов и всех приведенных выше кодов:

                      total_time_sec    ratio wrt fastest algo
searchsorted numpy:        0.0007        1.00
argmax numpy:              0.0009        1.29
for loop:                  0.0045        6.43
searchsorted pandas:       0.0075       10.71
idxmax pandas:             0.0267       38.14
index[0]:                  0.0295       42.14
first_valid_index pandas:  0.1181      168.71

Обратите внимание, что numy searchsorted является победителем, а first_valid_index показывает худшую производительность. Как правило, алгоритмы Numpy работают быстрее, и цикл for не так уж и плох, но только потому, что в кадре данных очень мало записей.

Для фрейма данных с 10 000 записей, где нужные записи ближе к концу, результаты отличаются, а сортировка поиска обеспечивает лучшую производительность:

                     total_time_sec ratio wrt fastest algo
searchsorted numpy:        0.0007       1.00
searchsorted pandas:       0.0076      10.86
argmax numpy:              0.0117      16.71
index[0]:                  0.0815     116.43
idxmax pandas:             0.0904     129.14
first_valid_index pandas:  0.1691     241.57
for loop:                  9.6504   13786.29

Код для получения этих результатов ниже:

import timeit

# code snippet to be executed only once 
mysetup = '''import pandas as pd
import numpy as np
df = pd.DataFrame({"A":['a','a','a','b','b'],"B":[1]*5})
'''

# code snippets whose execution time is to be measured   
mycode_set = ['''
df[df.A!='a'].first_valid_index()
''']
message = ["first_valid_index pandas:"]

mycode_set.append( '''df.loc[df.A!='a','A'].index[0]''')
message.append("index[0]: ")

mycode_set.append( '''df.A.ne('a').idxmax()''')
message.append("idxmax pandas: ")

mycode_set.append(  '''(df.A.values != 'a').argmax()''')
message.append("argmax numpy: ")

mycode_set.append( '''df.A.searchsorted('a', side='right')''')
message.append("searchsorted pandas: ")

mycode_set.append( '''df.A.values.searchsorted('a', side='right')''' )
message.append("searchsorted numpy: ")

mycode_set.append( '''for index in range(len(df['A'])):
    if df['A'][index] != 'a':
        ans = index
        break
        ''')
message.append("for loop: ")

total_time_in_sec = []
for i in range(len(mycode_set)):
    mycode = mycode_set[i]
    total_time_in_sec.append(np.round(timeit.timeit(setup = mysetup,\
         stmt = mycode, number = 100),4))

output = pd.DataFrame(total_time_in_sec, index = message, \
                      columns = ['total_time_sec' ])
output["ratio wrt fastest algo"] = \
np.round(output.total_time_sec/output["total_time_sec"].min(),2)

output = output.sort_values(by = "total_time_sec")
display(output)

Для больших данных:

mysetup = '''import pandas as pd
import numpy as np
n = 10000
lt = ['a' for _ in range(n)]
b = ['b' for _ in range(5)]
lt[-5:] = b
df = pd.DataFrame({"A":lt,"B":[1]*n})
'''

Ответ 3

Если вы строго хотите найти первый экземпляр, не пройдя весь файл данных, вы можете пойти в путь for-loop.

df = pd.DataFrame({"A":['a','a','a','b','b'],"B":[1]*5})
for index in range(len(df['A'])):
    if df['A'][index] != 'a':
        print(index)
        break

Индекс - это номер строки первого индекса, где df.A!= 'a'

Ответ 4

Для нескольких условий:

Допустим, у нас есть:

s = pd.Series(['a', 'a', 'c', 'c', 'b', 'd'])

И мы хотим найти первый элемент, отличный от a и c, мы делаем:

n = np.logical_and(s.values != 'a', s.values != 'c').argmax()

Времена:

import numpy as np
import pandas as pd
from datetime import datetime

ITERS = 1000

def pandas_multi_condition(s):
    ts = datetime.now()
    for i in range(ITERS):
        n = s[(s != 'a') & (s != 'c')].index[0]
    print(n)
    print(datetime.now() - ts)

def numpy_bitwise_and(s):
    ts = datetime.now()
    for i in range(ITERS):
        n = np.logical_and(s.values != 'a', s.values != 'c').argmax()
    print(n)
    print(datetime.now() - ts)

s = pd.Series(['a', 'a', 'c', 'c', 'b', 'd'])

print('pandas_multi_condition():')
pandas_multi_condition(s)
print()
print('numpy_bitwise_and():')
numpy_bitwise_and(s)

Выход:

pandas_multi_condition():
4
0:00:01.144767

numpy_bitwise_and():
4
0:00:00.019013

Ответ 5

Вы можете выполнять итерацию по строкам df (это медленно) и создавать собственную логику для получения значений, которые вы хотели:

def getMaxIndex(df, col)
    max = -999999
    rtn_index = 0
    for index, row in df.iterrows():
            if row[col] > max:
                max = row[col]
                rtn_index = index
    return rtn_index