programing

팬더 시리즈에서 입력된 숫자에 가장 가까운 값을 찾는 방법은 무엇입니까?

testmans 2023. 9. 12. 19:52
반응형

팬더 시리즈에서 입력된 숫자에 가장 가까운 값을 찾는 방법은 무엇입니까?

본 적이 있습니다.

이것들은 팬더가 아닌 바닐라 파이썬과 관련이 있습니다.

시리즈가 있는 경우:

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)를 구해야 합니다.

당신은 사용할 수 있습니다.argsort()맘에 들다

말합니다,input = 3

In [198]: input = 3

In [199]: df.iloc[(df['num']-input).abs().argsort()[:2]]
Out[199]:
   num
2    4
4    2

df_sort는 2개의 가장 가까운 값을 가지는 데이터 프레임입니다.

In [200]: df_sort = df.iloc[(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

질문에 완전히 대답하지 않는 것 외에도, 여기서 논의되는 다른 알고리즘의 추가적인 단점은 전체 목록을 정렬해야 한다는 것입니다.이렇게 되면 ~N log(N)의 복잡도가 발생합니다.

그러나 ~N에서도 동일한 결과를 얻을 수 있습니다.이 방법은 데이터 프레임을 원하는 값보다 작은 하나와 큰 두 개의 하위 집합으로 분리합니다.낮은 이웃은 작은 데이터 프레임에서 가장 큰 값보다 크며, 높은 이웃은 그 반대입니다.

이것은 다음과 같은 코드 스니펫을 제공합니다.

def find_neighbours(value, df, colname):
    exactmatch = df[df[colname] == value]
    if not exactmatch.empty:
        return exactmatch.index
    else:
        lowerneighbour_ind = df[df[colname] < value][colname].idxmax()
        upperneighbour_ind = df[df[colname] > value][colname].idxmin()
        return [lowerneighbour_ind, upperneighbour_ind] 

이 접근 방식은 팬더에서 파티션을 사용하는 것과 유사하며, 이는 대규모 데이터 세트를 다룰 때 정말 유용할 수 있으며 복잡성이 문제가 됩니다.


두 전략을 비교해 보면, 큰 N의 경우 분할 전략이 실제로 더 빠르다는 것을 알 수 있습니다.소규모 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')

저는 합니다를 사용하는 합니다.iloc.ix가 먼저 인덱스 레이블을 보기 때문에, 이것은 정렬되지 않은 정수 인덱스에서도 작동하기 때문에 John Galt의 답변에 추가합니다.

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

영상 시리즈가 이미 정렬되어 있는 경우, 인덱스를 찾는 효율적인 방법은 이등분 함수를 사용하는 것입니다.예:

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

그 열을 생각해 봅시다.col 프레임의터의의df 정렬됩니다.

  • 값이 경우.val열에요요bisect_left에는의한를고고를lne에한는의dnftxbisect_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

이등분 알고리즘은 데이터 프레임 열 "col" 또는 가장 가까운 이웃에서 특정 값 "val"의 인덱스를 찾는 데 매우 효율적이지만, 목록을 정렬해야 합니다.

시리즈가 이미 정렬되어 있다면 이와 같은 것을 사용할 수 있습니다.

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.

을 사용할 수 .numpy.searchsorted정렬되어 . 되지 된 의 을 할 이 할 을 의 된 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]

이러한 문제를 해결하기 위해 찾은 가장 직관적인 방법은 @ivo-merchiers가 제안한 파티션 접근 방식을 사용하되 가장 작고 가장 큰 n개를 사용하는 것입니다.정렬되지 않은 영상 시리즈에 대한 작업 외에도 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+1)
    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

에 을 에 가장 가까운 할 경우obj_num인에'num'과 선택지가 개일 일우과은른의을로적다을할수nsfncdt수nesreenn,dunsee이을러ee일적우'num'를 들어 열, 를 'num2'.

그러려면 새 열을 만드는 것이 좋습니다.'num_diff'그럼 쓰시오sort_values. : 에 을 하려고 을 하려고 에 3인에'num'열,열,은장은을다에을en에서 가장 작은 값을 합니다.'num2' 코드아래와 같은 코드:

import pandas as pd

obj_num = 3
df = pd.DataFrame({
    'num': [0, 1, 3, 3, 3, 4],
    'num2': [0, 0, 0, -1, 1, 0]
})

df_copy = df.loc[:, ['num', 'num2']].copy()
df_copy['num_diff'] = (df['num']-obj_num).abs()
df_copy.sort_values(
    by=['num_diff', 'num2'],
    axis=0,
    inplace=True
)
obj_num_idx = df_copy.index[0]

print(f'Objective row: \n{df.loc[obj_num_idx, :]}')

다음은 목표 값과 열의 딕트를 사용하여 작업을 수행하는 함수입니다(소트에 사용할 열의 순서를 존중합니다).

def colosest_row(df, obj):
    '''
    Sort df using specific columns given as obj keys.
    If a key has None value:
        sort column in ascending order.
    If a key has a float value:
        sort column from closest to farest value from obj[key] value.

    Arguments
    ---------
    df: pd.DataFrame
        contains at least obj keys in its columns.
    obj: dict
        dict of objective columns.
    
    Return
    ------
    index of closest row to obj
    '''
    df_copy = df.loc[:, [*obj]].copy()

    special_cols = []
    obj_cols = []
    for key in obj:
        if obj[key] is None:
            obj_cols.append(key)
        else:
            special_cols.append(key)
            obj_cols.append(f'{key}_diff')

    for key in special_cols:
        df_copy[f'{key}_diff'] = (df[key]-obj[key]).abs()

    df_copy.sort_values(
        by=obj_cols,
        axis=0,
        ascending=True,
        inplace=True
    )

    return df_copy.index[0]

obj_num_idx = colosest_row(
    df=df,
    obj={
        "num": obj_num,
        "num2": None  # Sort using also 'num2'
    }
)

여기에 많은 답이 있고 그 중에서도 꽤 좋습니다.아무 것도 인정되지 않으며 @Zero의 답변은 현재 가장 높은 평가를 받고 있습니다.또 다른 답변은 인덱스가 아직 정렬되지 않았을 때는 작동하지 않는다고 지적하지만, 더 이상 사용하지 않은 것처럼 보이는 솔루션을 추천합니다.

나는 내가 Numpy 버전을 사용할 수 있다는 것을 발견했습니다.argsort()인덱스가 정렬되지 않은 경우에도 작동하는 값 자체에 대해 다음과 같은 방식으로 사용할 수 있습니다.

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

문맥은 제로의 답을 참고하세요.

언급URL : https://stackoverflow.com/questions/30112202/how-do-i-find-the-closest-values-in-a-pandas-series-to-an-input-number

반응형