代码之家  ›  专栏  ›  技术社区  ›  steff

在pandas df[duplicate]中查找最接近给定值的列值

  •  0
  • steff  · 技术社区  · 6 年前

    我见过:

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

    如果我有这个系列:

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

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

    1. 如果在序列中找到3的索引
    2. 如果在序列中找不到该值,则该值在3上下的索引。

    也就是说,对于上面的序列{1,6,4,5,2},输入3,我应该得到带有索引(2,4)的值(4,2)。

    0 回复  |  直到 7 年前
        1
  •  31
  •   Zero    9 年前

    你可以用 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
  •  15
  •   user4647167    8 年前

    我建议使用 iloc 除了john galt的答案之外,因为这甚至适用于未排序的整数索引,因为 .ix 首先查看索引标签

    df.iloc[(df['num']-input).abs().argsort()[:2]]
    
        3
  •  5
  •   Ivo Merchiers kakajan    5 年前

    除了不能完全回答问题外,这里讨论的其他算法的一个额外缺点是它们必须对整个列表进行排序。这导致了 ~n日志(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
    

    这种方法类似于使用 partition in pandas ,这在处理大型数据集和复杂性成为问题时非常有用。


    对这两种策略的比较表明,对于大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
  •  2
  •   kztd    8 年前

    如果你的系列已经排序,你可以使用这样的东西。

    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
  •  1
  •   Maxence Bouvier    6 年前

    如果序列已经排序,则通过使用 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
    

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