繁体   English   中英

使用 dataframe 列对整个 dataframe 进行子集化,并按行应用 function(异常检测)

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM