python - recorrer - seleccionar columnas de un dataframe pandas
¿Cómo encuentro los valores más cercanos en una serie Pandas a un número de entrada? (5)
Además de no responder completamente la pregunta, una desventaja adicional de los otros algoritmos discutidos aquí es que tienen que ordenar la lista completa. Esto da como resultado una complejidad de ~ N log (N) .
Sin embargo, es posible lograr los mismos resultados en ~ N. Este enfoque separa el marco de datos en dos subconjuntos, uno más pequeño y otro más grande que el valor deseado. El vecino inferior es el valor más grande en el marco de datos más pequeño y viceversa para el vecino superior.
Esto proporciona el siguiente fragmento de código:
def find_neighbours(value):
exactmatch=df[df.num==value]
if !exactmatch.empty:
return exactmatch.index[0]
else:
lowerneighbour_ind = df[df.num<value].idxmax()
upperneighbour_ind = df[df.num>value].idxmin()
return lowerneighbour_ind, upperneighbour_ind
Este enfoque es similar al uso de la partición en pandas , que puede ser realmente útil cuando se trata de grandes conjuntos de datos y la complejidad se convierte en un problema.
La comparación de ambas estrategias muestra que para N grande, la estrategia de partición es de hecho más rápida. Para pequeñas N, la estrategia de clasificación será más eficiente, ya que se implementa en un nivel mucho más bajo. También es de una sola línea, lo que podría aumentar la legibilidad del código.
El código para replicar esta trama se puede ver a continuación:
from matplotlib import pyplot as plt
import pandas
import numpy
import timeit
value=3
sizes=numpy.logspace(2, 5, num=50, dtype=int)
sort_results, partition_results=[],[]
for size in sizes:
df=pandas.DataFrame({"num":100*numpy.random.random(size)})
sort_results.append(timeit.Timer("df.iloc[(df[''num'']-value).abs().argsort()[:2]].index",
globals={''find_neighbours'':find_neighbours, ''df'':df,''value'':value}).autorange())
partition_results.append(timeit.Timer(''find_neighbours(df,value)'',
globals={''find_neighbours'':find_neighbours, ''df'':df,''value'':value}).autorange())
sort_time=[time/amount for amount,time in sort_results]
partition_time=[time/amount for amount,time in partition_results]
plt.plot(sizes, sort_time)
plt.plot(sizes, partition_time)
plt.legend([''Sorting'',''Partitioning''])
plt.title(''Comparison of strategies'')
plt.xlabel(''Size of Dataframe'')
plt.ylabel(''Time in s'')
plt.savefig(''speed_comparison.png'')
He visto:
- ¿Cómo encuentro el valor más cercano a un número dado en una matriz?
- ¿Cómo encuentro el elemento de matriz más cercano a un número arbitrario (no miembro)? .
Estos se relacionan con la pitón de vainilla y no con los pandas.
Si tengo la serie:
ix num
0 1
1 6
2 4
3 5
4 2
Y yo ingreso 3, ¿cómo puedo encontrar (eficientemente)?
- El índice de 3 si se encuentra en la serie
- El índice del valor inferior y superior a 3 si no se encuentra en la serie.
Es decir. Con la serie anterior {1,6,4,5,2}, y la entrada 3, debería obtener valores (4,2) con índices (2,4).
Podrías usar
argsort()
como
Digamos,
input = 3
In [198]: input = 3
In [199]: df.ix[(df[''num'']-input).abs().argsort()[:2]]
Out[199]:
num
2 4
4 2
df_sort
es el marco de datos con 2 valores más cercanos.
In [200]: df_sort = df.ix[(df[''num'']-input).abs().argsort()[:2]]
Para el índice,
In [201]: df_sort.index.tolist()
Out[201]: [2, 4]
Para los valores,
In [202]: df_sort[''num''].tolist()
Out[202]: [4, 2]
Detalle, para la solución anterior
df
fue
In [197]: df
Out[197]:
num
0 1
1 6
2 4
3 5
4 2
Recomiendo usar
iloc
además de la respuesta de John Galt ya que esto funcionará incluso con un índice entero sin clasificar, ya que
.ix
primero mira las etiquetas de índice
df.iloc[(df[''num'']-input).abs().argsort()[:2]]
Si la serie ya está ordenada, un método eficiente para encontrar los índices es usar bisect . Un ejemplo:
idx = bisect_right(df[''num''].values, 3)
Entonces, para el problema citado en la pregunta, considerando que la columna "col" del marco de datos "df" está ordenada:
from bisect import bisect_right, bisect_left
def get_closests(df, col, val):
lower_idx = bisect_right(df[col].values, val)
higher_idx = bisect_left(df[col].values, val)
if higher_idx == lower_idx:
return lower_idx
else:
return lower_idx, higher_idx
Es bastante eficiente encontrar el índice del valor específico "val" en la columna del marco de datos "col", o sus vecinos más cercanos, pero requiere que se ordene la lista.
Si su serie ya está ordenada, podría usar algo como esto.
def closest(df, col, val, direction):
n = len(df[df[col] <= val])
if(direction < 0):
n -= 1
if(n < 0 or n >= len(df)):
print(''err - value outside range'')
return None
return df.ix[n, col]
df = pd.DataFrame(pd.Series(range(0,10,2)), columns=[''num''])
for find in range(-1, 2):
lc = closest(df, ''num'', find, -1)
hc = closest(df, ''num'', find, 1)
print(''Closest to {} is {}, lower and {}, higher.''.format(find, lc, hc))
df: num
0 0
1 2
2 4
3 6
4 8
err - value outside range
Closest to -1 is None, lower and 0, higher.
Closest to 0 is 0, lower and 2, higher.
Closest to 1 is 0, lower and 2, higher.