Замените значение значением ближайшего соседа в фрейме данных Pandas

У меня проблема с получением ближайших значений для некоторых строк в фрейме данных pandas и заполнением другого столбца значениями из этих строк.

образец данных у меня есть:

id   su_id  r_value  match_v

A      A1      0        1
A      A2      0        1
A      A3      70       2
A      A4      120      100
A      A5      250      3
A      A6      250      100
B      B1      0        1
B      B2      30       2

Дело в том, что везде, где match_v равно 100, мне нужно заменить это 100 значением из строки, где r_value ближе всего к r_value из исходной строки (где match_v равно 100), но только с группой (сгруппированной по id )

Ожидаемый результат

id   su_id  r_value  match_v

A      A1      0        1
A      A2      0        1
A      A3      70       2
A      A4      120      2
A      A5      250      3
A      A6      250      3
B      B1      0        1
B      B2      30       2

Я пытался создать ход и ногу со сдвигом, а затем найти различия. Но работает плохо и как-то испортил уже хорошие значения. Я не пробовал ничего другого, потому что я действительно понятия не имею.

Любая помощь или подсказка приветствуются, и если вам нужна дополнительная информация, я здесь.

Заранее спасибо.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
8
0
2 599
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете определить пользовательскую функцию, которая выполняет расчет и замену, а затем использовать ее с groupby и применить.

def mysubstitution(x):
    for i in x.index[x['match_v'] == 100]:
        diff = (x['r_value'] - (x['r_value'].iloc[i])).abs()
        exclude = x.index.isin([i])
        closer_idx = diff[~exclude].idxmin()
        x['match_v'].iloc[i] = x['match_v'].iloc[closer_idx]
    return x

ddf = df.groupby('id').apply(mysubstitution)

ddf это:

  id su_id  r_value  match_v
0  A    A1        0        1
1  A    A2        0        1
2  A    A3       70        2
3  A    A4      120        2
4  A    A5      250        3
5  A    A6      250        3
6  B    B1        0        1
7  B    B2       30        2

Отличная логика и очень хороший ответ. +1

Scott Boston 30.05.2019 15:53
Ответ принят как подходящий

Больше похоже на merge_asof

s=df.loc[df.match_v!=100]
s=pd.merge_asof(df.sort_values('r_value'),s.sort_values('r_value'),on='r_value',by='id',direction='nearest')
df['match_v']=df['su_id'].map(s.set_index('su_id_x')['match_v_y'])
df
Out[231]: 
  id su_id  r_value  match_v
0  A    A1        0        1
1  A    A2        0        1
2  A    A3       70        2
3  A    A4      120        2
4  A    A5      250        3
5  A    A6      250        3
6  B    B1        0        1
7  B    B2       30        2

Вот еще один способ использовать numpy трансляцию, построить для ускорения расчета

l=[]
for x , y in df.groupby('id'): 
    s1=y.r_value.values
    s=abs((s1-s1[:,None])).astype(float)
    s[np.tril_indices(s.shape[0], 0)] = 999999
    s=s.argmin(0)
    s2=y.match_v.values
    l.append(s2[s][s2==100])
df.loc[df.match_v==100,'match_v']=np.concatenate(l)
df
Out[264]: 
  id su_id  r_value  match_v
0  A    A1        0        1
1  A    A2        0        1
2  A    A3       70        2
3  A    A4      120        2
4  A    A5      250        3
5  A    A6      250        3
6  B    B1        0        1
7  B    B2       30        2
merge_asof с direction='nearest'... гениально! +1
Scott Boston 30.05.2019 16:09

Спасибо за помощь. Я получаю ValueError: левые ключи должны быть отсортированы, но они уже отсортированы в вашем коде. Может быть знаете, что может быть причиной этого? После того, как я удалил несколько строк с nan в r_value, я получаю InvalidIndexError: переиндексация действительна только с объектами Index с уникальным значением.

jovicbg 30.05.2019 16:36

Я удалил дубликаты, и теперь это работает. Спасибо, большое спасибо.

jovicbg 30.05.2019 16:39

Предполагая, что всегда есть хотя бы одно действительное значение в группе, когда 100 встречается впервые.

m = dict()
for i in range(len(df)):
    if df.loc[i, "match_v"] == 100:
        df.loc[i, "match_v"] = m[df.loc[i, "id"]]
    else:
        m[df.loc[i, "id"]] = df.loc[i, "match_v"]

Другие вопросы по теме