0

我有一个熊猫数据框。对于每一行,我想知道变量是如何存在的outlier。为简单起见,假设我将异常值定义为每列记录的顶部(底部)5% 值中的观察值。

换句话说,我想知道:

  1. 对于每一列,找出最高 5% 的记录值(如果观察值位于给定列的前 5%,则返回 1,否则返回 0)
  2. 逐行求和
  3. 将标识 的列添加number of outliers per row到原始数据集

如何在 python 中以速度和内存高效的方式做到这一点?

使用 R 的示例:

让我们有如下数据集:

   ID v1 v2 v3
1:  a  1  2  0
2:  b  2  3  0
3:  c  1  6  1
4:  d  3  1  2
5:  e  4  0  3
6:  f  5  2  5

# set up a reproducible example
library(data.table)
df = data.table(ID = c('a', 'b', 'c', 'd', 'e', 'f'),
                v1 = c(1,2,1,3,4,5),
                v2 = c(2,3,6,1,0,2),
                v3 = c(0,0,1,2,3,5))

# function to find out the outliers
outlier_detector = function(x, type = 'positive',tail = 0.05)
{
  if (type == 'positive')
  {
    x >= quantile(x,  1 - tail)
  }
  else if (type == 'negative')
  {
    x <= quantile(x, tail)
  }
}

# add two columns to the original dataset
# sum_out_positive - for each row calculates the number of columns where within top 5%
# sum_out_negative - for each row calculates the number of columns where within bottom 5%
df[,`:=`(
  sum_out_positive = df[,2:4][
    ,
    lapply(.SD, outlier_detector)][
      ,
      rowSums(.SD, na.rm = T),
      .SDcols = paste0('v', 1:3)],
  sum_out_negative = df[, 2:4][
    ,
    lapply(.SD, outlier_detector, 'negative')][
      ,
      rowSums(.SD, na.rm = T),
      .SDcols = paste0('v', 1:3)])]

预期输出:

   ID v1 v2 v3 sum_out_positive sum_out_negative
1:  a  1  2  0                0                2
2:  b  2  3  0                0                1
3:  c  1  6  1                1                1
4:  d  3  1  2                0                0
5:  e  4  0  3                0                1
6:  f  5  2  5                2                0

在 python 中实现这一点的有效方法是什么?我知道我可以编写一个循环来迭代所有列,并根据观察结果是否为异常值,为每个观察结果返回 True/False,然后执行逐行求和(使用 df.sum(axis = 1))。

但是我可以在不创建另一个与原始数据帧大小相同的数据帧的情况下执行此操作,然后在第二步中执行求和吗?即我想优化速度以及执行计算所需的内存量。

奖励问题:如何改进我在 R 中的计算?

编辑: 我想我可以在 python pandas 中做类似的事情:

(df.iloc[:, 1:3] >= df.iloc[:,1:3].quantile(0.95, axis = 0)).sum(axis = 1)

但这是最好的方法吗?

4

1 回答 1

0

这是一个解决方案,可能不是最优雅的方式,也不是最优化的方式,但它确实有效。希望能帮助到你:

# For each value column, indicate the outliers
for col in df.columns[1:]:
    df[f'{col}_outliers_pos'] = np.where(df[col] >= df[col].quantile(0.95), 1, 0)
    df[f'{col}_outliers_neg'] = np.where(df[col] <= df[col].quantile(0.05), 1, 0)

# Create lists for positive and negative columns 
pos_cols = [col for col in df.columns if 'pos' in col]
neg_cols = [col for col in df.columns if 'neg' in col]

# Calculate the sum of both negative and positive
df['sum_out_positive'] = df[pos_cols].sum(axis=1)
df['sum_out_negative'] = df[neg_cols].sum(axis=1)

# Drop columns we dont need to get correct output
df.drop(pos_cols + neg_cols, axis=1, inplace=True)

print(df)
  ID  v1  v2  v3  sum_out_positive  sum_out_negative
0  a   1   2   0                 0                 2
1  b   2   3   0                 0                 1
2  c   1   6   1                 1                 1
3  d   3   1   2                 0                 0
4  e   4   0   3                 0                 1
5  f   5   2   5                 2                 0
于 2019-03-10T13:57:36.553 回答