No olvides que en la informática cualquier problema casi siempere suele tener más de una soluciones, por lo tanto nosotros te mostramos la mejor y más eficiente.
Solución:
idxmax
y argmax
devolverá la posición del valor máximo o la primera posición si el valor máximo se produce más de una vez.
utilizar idxmax
en df.A.ne('a')
df.A.ne('a').idxmax()
3
o la numpy
equivalente
(df.A.values != 'a').argmax()
3
Sin embargo, si A
ya ha sido ordenado, entonces podemos usar searchsorted
df.A.searchsorted('a', side='right')
array([3])
O la numpy
equivalente
df.A.values.searchsorted('a', side='right')
3
Descubrí que hay una función first_valid_index para Pandas DataFrames que hará el trabajo, se podría usar de la siguiente manera:
df[df.A!='a'].first_valid_index()
3
Sin embargo, esta función parece ser muy lenta. Incluso tomar el primer índice del marco de datos filtrado es más rápido:
df.loc[df.A!='a','A'].index[0]
A continuación, comparo el tiempo total (seg) de repetir los cálculos 100 veces para estas dos opciones y todos los códigos anteriores:
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
Observe que numpy’s searchsorted es el ganador y first_valid_index muestra el peor rendimiento. En general, los algoritmos numpy son más rápidos y el ciclo for no funciona tan mal, pero es solo porque el marco de datos tiene muy pocas entradas.
Para un marco de datos con 10 000 entradas donde las entradas deseadas están más cerca del final, los resultados son diferentes, y la búsqueda ordenada ofrece el mejor rendimiento:
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
El código para producir estos resultados es el siguiente:
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)
Para el marco de datos más grande:
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)
'''
Recuerda que puedes difundir esta sección si lograste el éxito.