Как найти ближайшие значения в серии Pandas для ввода номера?
Я видел:
Они относятся к ванильному питону, а не к pandas.
Если у меня есть ряд:
ix num
0 1
1 6
2 4
3 5
4 2
И я ввожу 3, , как я могу (эффективно) найти?
- Индекс 3, если он найден в серии
- Индекс значения ниже и выше 3, если он не найден в серии.
Т.е. С приведенными выше рядами {1,6,4,5,2} и ввода 3 я должен получить значения (4,2) с индексами (2,4).
Ответы
Ответ 1
Вы можете использовать argsort()
как
Скажем, 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
- это кадр данных с двумя ближайшими значениями.
In [200]: df_sort = df.ix[(df['num']-input).abs().argsort()[:2]]
Для индекса,
In [201]: df_sort.index.tolist()
Out[201]: [2, 4]
Для значений
In [202]: df_sort['num'].tolist()
Out[202]: [4, 2]
Подробно, для вышеуказанного решения df
было
In [197]: df
Out[197]:
num
0 1
1 6
2 4
3 5
4 2
Ответ 2
Я рекомендую использовать iloc
в дополнение к ответу Джона Галта, поскольку это будет работать даже с несортированным целочисленным индексом, поскольку .ix сначала просматривает метки индекса
df.iloc[(df['num']-input).abs().argsort()[:2]]
Ответ 3
Помимо не полного ответа на этот вопрос, дополнительным недостатком других алгоритмов, обсуждаемых здесь, является то, что они должны сортировать весь список. Это приводит к сложности ~ N log (N).
Тем не менее, можно достичь тех же результатов в ~ N. Этот подход разделяет фрейм данных на два подмножества: одно меньше, а другое больше желаемого значения. Нижний сосед имеет наибольшее значение в меньшем кадре данных, и наоборот для верхнего соседа.
Это дает следующий фрагмент кода:
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
Этот подход аналогичен использованию раздела в пандах, который может быть очень полезен при работе с большими наборами данных, когда возникает сложность.
Сравнение обеих стратегий показывает, что для больших N стратегия разделения действительно быстрее. Для малого N стратегия сортировки будет более эффективной, поскольку она реализована на гораздо более низком уровне. Это также однострочник, который может улучшить читаемость кода.
![Comparison of partitioning vs sorting]()
Код для воспроизведения этого сюжета можно увидеть ниже:
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')
Ответ 4
Если ваша серия уже отсортирована, вы можете использовать что-то вроде этого.
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.
Ответ 5
Если ряды уже отсортированы, эффективный метод поиска индексов - использование функций bisect.
Пример:
idx = bisect_left(df['num'].values, 3)
Давайте рассмотрим, что столбец col
кадра данных df
отсортирован.
- В случае, когда значение
val
находится в столбце, bisect_left
вернет точный индекс значения в списке и
bisect_right
вернет индекс следующей позиции.
- В случае, если значение отсутствует в списке, оба
bisect_left
и bisect_right
вернет тот же индекс: тот, где
введите значение, чтобы сохранить список отсортированным.
Следовательно, чтобы ответить на вопрос, следующий код дает индекс val
в col
, если он найден, и индексы ближайших значений в противном случае. Это решение работает, даже если значения в списке не являются уникальными.
from bisect import bisect_left, bisect_right
def get_closests(df, col, val):
lower_idx = bisect_left(df[col].values, val)
higher_idx = bisect_right(df[col].values, val)
if higher_idx == lower_idx: #val is not in the list
return lower_idx - 1, lower_idx
else: #val is in the list
return lower_idx
Алгоритмы Bisect очень эффективны для нахождения индекса определенного значения "val" в столбце "col" блока данных или его ближайших соседей, но для этого требуется сортировка списка.