Определение ближайшего значения в столбце для каждого фильтра с использованием Pandas

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

Например, если ввод был определен в приведенном ниже коде, вывод должен иметь только (a, 1, True), (b, 2, True), (c, 2, True), а все остальные isClosest Values должны иметь значение False.

Если несколько ближайших значений, то это должно быть первое из перечисленных значений.

Вот код, который у меня есть, но я не могу заставить его правильно примениться к фрейму данных. Я хотел бы несколько указателей.

df = pd.DataFrame()
df['category'] = ['a', 'b', 'b', 'b', 'c', 'a', 'b', 'c', 'c', 'a']
df['values'] = [1, 2, 3, 4, 5, 4, 3, 2, 1, 0]
df['isClosest'] = False

uniqueCategories = df['category'].unique()
for c in uniqueCategories:
    filteredCategories = df[df['category']==c]    
    sortargs = (filteredCategories['value']-2.0).abs().argsort()
    #how to use sortargs so that we set column in df isClosest=True if its the closest value in each category to 2.0?

person Scott Chamberlin    schedule 11.10.2016    source источник


Ответы (2)


Вы можете создать столбец абсолютных разностей:

df['dif'] = (df['values'] - 2).abs()

df
Out: 
  category  values  dif
0        a       1    1
1        b       2    0
2        b       3    1
3        b       4    2
4        c       5    3
5        a       4    2
6        b       3    1
7        c       2    0
8        c       1    1
9        a       0    2

А затем используйте groupby.transform, чтобы проверить, равно ли минимальное значение каждой группы рассчитанной вами разнице:

df['is_closest'] = df.groupby('category')['dif'].transform('min') == df['dif']

df
Out: 
  category  values  dif is_closest
0        a       1    1       True
1        b       2    0       True
2        b       3    1      False
3        b       4    2      False
4        c       5    3      False
5        a       4    2      False
6        b       3    1      False
7        c       2    0       True
8        c       1    1      False
9        a       0    2      False

df.groupby('category')['dif'].idxmin() также даст вам индексы ближайших значений для каждой категории. Вы также можете использовать это для картирования.

Для выбора:

df.loc[df.groupby('category')['dif'].idxmin()]
Out: 
  category  values  dif
0        a       1    1
1        b       2    0
7        c       2    0

Для назначения:

df['is_closest'] = False
df.loc[df.groupby('category')['dif'].idxmin(), 'is_closest'] = True
df
Out: 
  category  values  dif is_closest
0        a       1    1       True
1        b       2    0       True
2        b       3    1      False
3        b       4    2      False
4        c       5    3      False
5        a       4    2      False
6        b       3    1      False
7        c       2    0       True
8        c       1    1      False
9        a       0    2      False

Разница между этими подходами заключается в том, что если вы проверяете равенство на разницу, вы получите True для всех строк в случае равенства. Однако с idxmin он вернет True для первого вхождения (только один для каждой группы).

person ayhan    schedule 11.10.2016

Решение с DataFrameGroupBy.idxmin — получить индексы минимальных значений для каждой группы, а затем назначьте логическую маску с помощью Index.isin в столбец isClosest:

idx = (df['values'] - 2).abs().groupby([df['category']]).idxmin()
print (idx)
category
a    0
b    1
c    7
Name: values, dtype: int64

df['isClosest'] = df.index.isin(idx)
print (df)
  category  values isClosest
0        a       1      True
1        b       2      True
2        b       3     False
3        b       4     False
4        c       5     False
5        a       4     False
6        b       3     False
7        c       2      True
8        c       1     False
9        a       0     False
person jezrael    schedule 11.10.2016