假设我有一个像这样的pandas数据框:
cat val
0 a 1
1 a 6
2 a 12
3 b 2
4 b 5
5 b 11
6 c 4
7 c 22
我想知道,对于每个类别('cat'的每个值),值最接近给定值的位置是什么,比如说5.5。我可以减去我的目标值并取绝对值,给我这样的东西:
cat val val_delt
0 a 1 4.5
1 a 6 0.5
2 a 12 6.5
3 b 2 3.5
4 b 5 0.5
5 b 11 5.5
6 c 4 1.5
7 c 22 16.5
但是我被困在下一步去哪里。我的第一个想法是使用argmin()和groupby(),但这会产生错误:
In [375]: df.groupby('cat').val_delt.argmin()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-375-a2c3dbc43c50> in <module>()
----> 1 df.groupby('cat').val_delt.argmin()
TypeError: 'Series' object is not callable
当然,我可以在标准python中提出一些可怕的hacky事情,我迭代cat的所有值,然后选择与该值对应的数据子集,执行argmin操作然后找出其中的位置该行的原始数据帧。但是必须有一种更优雅的方式来做到这一点。
我想要的输出是这样的:
cat val
1 a 6
4 b 5
6 c 4
或至少包含相关信息的一些结构(例如 - {'a':1,'b':4,'c':6})。如果我找回索引值或索引位置,我不在乎,但我需要两个中的一个。我不关心回到这个值 - 一旦我有索引子集,我总能得到它。
答案 0 :(得分:5)
argmin()
不是聚合函数,您可以使用apply来获取每个组的最近索引:
txt = """ cat val
0 a 1
1 a 6
2 a 12
3 b 2
4 b 5
5 b 11
6 c 4
7 c 22"""
import io
df = pd.read_csv(io.BytesIO(txt), delim_whitespace=True, index_col=0)
df["val_delt"] = (df.val - 5.5).abs()
idx = df.groupby("cat").apply(lambda df:df.val_delt.argmin())
df.ix[idx, :]
输出:
cat val val_delt
1 a 6 0.5
4 b 5 0.5
6 c 4 1.5
答案 1 :(得分:4)
只需添加到HYRY的答案,即可使用idxmin。 例如:
import io
txt = """ cat val
0 a 1
1 a 6
2 a 12
3 b 2
4 b 5
5 b 11
6 c 4
7 c 22"""
df = pd.read_csv(io.BytesIO(txt.encode()), delim_whitespace=True, index_col=0)
df["val_delt"] = (df.val - 5.5).abs()
idx = df.groupby("cat").apply(lambda df:df.val_delt.idxmin())
df.ix[idx, :]
答案 2 :(得分:0)
您不需要申请。
idxmin
就足够了。只需确保已设置所需的最小值索引即可。
>>> df['val_delt'] = (df.val - 5.5).abs()
>>> df.set_index('val').groupby('cat').idxmin()
val_delt
cat
a 6
b 5
c 4
答案 3 :(得分:0)
您可以将df.groupby('cat').val_delt.argmin()
替换为df.sort_values(['cat', 'val_delt']).groupby('cat').head(1)
。本质上,这是按两列(cat
,然后是val_delt
)对DataFrame进行排序。
代码
df = pd.DataFrame([['a', 1], ['a', 6], ['a', 12], ['b', 2], ['b', 5], ['b', 11], ['c', 4], ['c', 22]], columns=['cat', 'val'])
df['val_delt'] = (df.val - 5.5).abs()
df.sort_values(['cat', 'val_delt']).groupby('cat').head(1)
结果
cat val val_delt
1 a 6 0.5
4 b 5 0.5
6 c 4 1.5
答案 4 :(得分:-1)
这里的所有答案在某种程度上都是正确的,但是没有一个答案是简洁,美观和精巧的。我在这里留下了清晰的方法。
>>> indx = df.groupby('cat')['val_delt'].idxmin()
>>> df.loc[indx]
cat val val_delt
1 a 6 0.5
4 b 5 0.5
6 c 4 1.5