如何在Pandas系列中找到与输入数字最接近的值?

时间:2015-05-07 21:43:21

标签: python pandas dataframe ranking

我见过:

这些与香草蟒蛇有关,而不是熊猫。

如果我有这个系列:

ix   num  
0    1
1    6
2    4
3    5
4    2

我输入3,我怎样才能(有效地)找到?

  1. 如果在系列
  2. 中找到,则索引为3
  3. 如果在系列中找不到,则低于和高于3的值的索引。
  4. IE中。使用上面的系列{1,6,4,5,2}和输入3,我应该得到带有索引(2,4)的值(4,2)。

8 个答案:

答案 0 :(得分:30)

您可以使用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是具有2个最接近值的数据框。

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

答案 1 :(得分:15)

除了John Galt的回答之外,我建议使用iloc,因为即使使用未排序的整数索引,这也会有效,因为.ix首先查看索引标签

df.iloc[(df['num']-input).abs().argsort()[:2]]

答案 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>traversed].idxmin()
            return lowerneighbour_ind, upperneighbour_ind

此方法类似于使用partition in pandas,当处理大型数据集且复杂性成为问题时,它确实非常有用。

答案 3 :(得分:2)

如果您的系列已经排序,您可以使用类似的东西。

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.

答案 4 :(得分:1)

如果系列已经排序,则使用bisect是查找索引的有效方法。 一个例子:

idx = bisect_right(df['num'].values, 3)

因此对于问题中提到的问题,考虑对数据帧“ df”的列“ col”进行排序:

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

在数据框列“ col”或其最接近的邻居中找到特定值“ val”的索引是非常有效的,但是它要求对列表进行排序。

答案 5 :(得分:0)

您可以使用numpy.searchsorted。如果您的搜索列尚未排序,则可以创建一个已排序的DataFrame并使用pandas.argsort记住它们之间的映射。 (如果您计划多次查找最接近的值,则比上述方法要好。)

对它进行排序后,就可以像这样找到最接近的输入值:

indLeft = np.searchsorted(df['column'], input, side='left')
indRight = np.searchsorted(df['column'], input, side='right')

valLeft = df['column'][indLeft]
valRight = df['column'][indRight]

答案 6 :(得分:0)

这里有很多答案,其中很多都很好。没有人接受,并且@Zero的答案是目前最高的评价。另一个答案指出,当尚未对索引进行排序时,它不起作用,但是他/她建议使用一种似乎已过时的解决方案。

我发现我可以按以下方式在值本身上使用argsort()的numpy版本,即使索引未排序也可以使用:

df.iloc[(df['num']-input).abs()..values.argsort()[:2]]

有关上下文,请参见Zero的答案。

答案 7 :(得分:0)

我发现解决此类问题的最直观方法是使用@ivo-merchiers 建议的分区方法,但使用 nsmallest 和 nlargest。除了处理未排序的系列之外,这种方法的一个好处是您可以通过将 k_matches 设置为大于 1 的数字轻松获得多个接近值。

import pandas as pd
source = pd.Series([1,6,4,5,2])
target = 3

def find_closest_values(target, source, k_matches=1):
    k_above = source[source >= target].nsmallest(k_matches)
    k_below = source[source < target].nlargest(k_matches)
    k_all = pd.concat([k_below, k_above]).sort_values()
    return k_all

find_closest_values(target, source, k_matches=1)

输出:

4    2
2    4
dtype: int64