панды находят первое появление
Предположим, что у меня есть структурированный 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