私は見た:
これらは、パンダではなくバニラpythonに関連しています。
シリーズがある場合:
ix num
0 1
1 6
2 4
3 5
4 2
そして、3を入力しますどうすれば(効率的に)見つけることができますか?
すなわち。上記のシリーズ{1,6,4,5,2}、および入力3では、インデックス(2,4)で値(4,2)を取得する必要があります。
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
。ix はインデックスラベルを最初に見るため、ソートされていない整数インデックスでも機能するため、John Galtの答えに加えてiloc
を使用することをお勧めします。
df.iloc[(df['num']-input).abs().argsort()[:2]]
質問に完全に答えていないこととは別に、ここで説明した他のアルゴリズムの追加の欠点は、リスト全体をソートする必要があることです。これにより、〜N log(N)の複雑さが生じます。
ただし、〜Nで同じ結果を達成することは可能です。このアプローチでは、データフレームを2つのサブセットに分けます。1つは目的の値よりも小さく、もう1つは大きくなります。下位のデータフレームの最大値よりも下位のノードが近く、上位の隣接ノードの場合はその逆です。
これにより、次のコードスニペットが得られます。
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
このアプローチは pandasのパーティション を使用するのと似ています。これは、大きなデータセットを処理する際に非常に役立ち、複雑さが問題になります。
両方の戦略を比較すると、Nが大きい場合、パーティション戦略が実際に高速であることがわかります。小さいNの場合、ソート戦略はより低いレベルで実装されるため、より効率的です。また、1行であるため、コードが読みやすくなります。
このプロットを再現するコードは次のとおりです。
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')
シリーズが既にソートされている場合は、このようなものを使用できます。
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.
シリーズが既にソートされている場合、インデックスを見つける効率的な方法は 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」のインデックスを見つけることは非常に効率的ですが、リストをソートする必要があります。