[英]Use dataframe column to subset entire dataframe and apply function row-wise (anomaly detection)
假设我有一张这样的桌子
代码 | 邻居 | t_min |
---|---|---|
abr007 | abr018, abr030, abr032 | 16.2 |
abr018 | abr007、abr030、laz246 | 20.9 |
abr030 | abr007, abr032, cmp015 | 21.9 |
... | ... | ... |
它包含一些气象站的温度观测结果,这些气象站具有由code
给出的唯一 ID。
为了找到异常值,我添加了一个包含相邻站点( neighbours
)的列,以便将每个站点的值与相邻观测值给出的分布进行比较:如果它落在 [1st, 99th] 百分位数区间之外,我将其标记为异常。
我通过遍历行很容易地实现了这一点,然后对原始 dataframe df[df.code.isin(row.neighbors)]
进行子集化,计算该子集的百分位数并将其与站观测值进行比较。 但是,这相对较慢(大约 8 秒,约 3000 行),我觉得我可以通过将原始 dataframe 分组到code
上来摆脱循环并矢量化操作。
几天来我一直在考虑这个问题,无法想出一个简单的解决方案来将相同的操作包装到 function 中,在分组时表现良好......这可能吗?
- - 细节 - -
这是我目前使用的代码片段
for i, row in df.iterrows():
# Grab all the neighbours and take care of
# - excluding the station itself
# - excluding any station that has already been flagged as anomalous
subset = df[(df.code.isin(row.neighbors)) & (
df.code != row.code) & (~df['anomaly'])]
q1, q99 = subset['t_min'].quantile([qn1, qn2]).values
if ((df.loc[df.code == row.code,'t_min'].item() < q1) or
(df.loc[df.code == row.code, 't_min'].item() > q99)):
df.loc[df.code == row.code,'anomaly'] = True
Neibhours 是使用BallTree
算法找到的(这部分实际上非常快。)。
我通过创建临时 arrays 找到了更好的方法。 如果你考虑
variable = df['t_min'].values
indices
保存每个站点的邻居的所有索引然后我只是事先计算分位数
var_quantiles = [np.nanquantile(variable[ind], [qn1, qn2]) if len(
ind) > 5 else np.array([-np.inf, np.inf]) for ind in indices]
然后构造一个pd.Interval
列,其中包含分位数给出的限制
df['interval'] = pd.IntervalIndex.from_arrays(np.array(var_quantiles).T[0] - thresh,
np.array( var_quantiles).T[1] + thresh,
closed='both')
然后我可以单独检查超出区间的站点
df[anomaly_column_name] = df.apply(
lambda x: True if x[var] not in x['interval'] and not np.isnan(x[var]) else False, axis=1)
这需要不到一秒钟而不是 12 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.