为每行计算在前X%内观察到多少列

时间:2019-03-10 10:29:30

标签: python r pandas

我有一个熊猫数据框。对于每一行,我想了解变量如何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熊猫中做类似的事情:

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

但这是最好的方法吗?

1 个答案:

答案 0 :(得分: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