在r中使用by和rbind函数时如何维护行元素的顺序?

时间:2016-06-23 23:24:17

标签: r function sorting outliers rbind

我编写了一个函数,它根据name列的值来获取数据的子集。它计算列的异常值" mark"并取代所有异常值。 但是,当我尝试组合这些不同的子集时,我的元素的顺序会发生变化。有什么方法可以维持我的元素在列中的顺序"标记"

我的数据集是:

 name   mark  
    A  100.0   
    B  0.5   
    C  100.0  
    A  50.0   
    B  90.0   
    B  1000.0   
    C  1200.0   
    C  5000.0   
    A  210.0 

我写的功能是:

data.frame(do.call("rbind", as.list(by(data, data$name, 
   function(x){apply(x[, .(mark)],2, 
   function(y) {y[y > (quantile(x$mark, na.rm=TRUE)[[3]][[1]] + 1.5 * IQR(x$mark))] 
   <- (quantile(x$mark, na.rm=TRUE)[[3]][[1]] + 1.5 * IQR(x$mark));y})}))))

上述功能的结果是下面的第一列(为了说明目的,我手动添加了name):

      mark      NAME
100.000 ----- A
50.000 ----- A
210.000 ----- A
0.500 ----- B
90.000 ----- B
839.625 ----- B
100.000 ----- C
1200.000 ----- C
4875.000 ----- C

在上面的结果中,标记列的值的顺序被改变。有什么方法可以维持元素的顺序吗?

2 个答案:

答案 0 :(得分:1)

您确定代码正在按照您的想法行事吗?

看起来你正在用中位数+ 1.5 * IQR替换大于中位数(第三个返回值quantile)的任何值。也许这就是你想要的,我不知道。更大的问题是您在apply函数中执行此操作,因此它将重新计算每次迭代的中位数和IQR,并使用之前已更改的行进行更新。我打赌这不是你想要的,但我想我已经看到了陌生人。

更好的选择可能是创建一个外部函数来完成工作,它接收所有数据,进行计算,然后输出所有数据。我之所以喜欢dplyr,只是因为它很干净。

读取您的数据(为什么&#34; ----&#34;?)

scores <- read.table(text="
name   mark  
A 100.0   
B 0.5   
C 100.0  
A 50.0   
B 90.0   
B 1000.0   
C 1200.0   
C 5000.0   
A 210.0", header=TRUE)

并创建一个更有意义的功能;替换任何大于75%分位数的值(由名称引用,因此您知道它是什么)或小于具有该限制值的25%分位数

scale_outliers <- function(data) {

  lim <- quantile(data, na.rm = TRUE)
  data[data > lim["75%"]] <- lim["75%"]
  data[data < lim["25%"]] <- lim["25%"]
  return(data)

}

将此处理链接到dplyr::mutate很简洁,然后可以传递给ggplot。这是原始数据

gg1 <- scores %>% ggplot(aes(x=name, y=mark)) 
gg1 <- gg1 + geom_point() + geom_boxplot() + coord_cartesian(ylim=range(scores$mark))
gg1

gg1

如果我们使用新函数对其进行更改,我们将返回数据,而不会在

周围更改行
scores %>% mutate(new_mark = scale_outliers(mark))
#>   name   mark new_mark
#> 1    A  100.0      100
#> 2    B    0.5       90
#> 3    C  100.0      100
#> 4    A   50.0       90
#> 5    B   90.0       90
#> 6    B 1000.0     1000
#> 7    C 1200.0     1000
#> 8    C 5000.0     1000
#> 9    A  210.0      210

我们可以绘制,

gg2 <- scores %>% mutate(new_mark = scale_outliers(mark)) %>% ggplot(aes(x=name, y=new_mark)) 
gg2 <- gg2 + geom_point() + geom_boxplot()  + coord_cartesian(ylim=range(scores$mark))
gg2

gg2

最重要的是,如果你现在想要按分组进行分位数比较(比如name列,那就像使用dplyr::group_by(name)一样简单,

gg3 <- scores %>% group_by(name) %>% mutate(new_mark = scale_outliers(mark)) %>% ggplot(aes(x=name, y=new_mark)) 
gg3 <- gg3 + geom_point() + geom_boxplot()  + coord_cartesian(ylim=range(scores$mark))
gg3

gg3

答案 1 :(得分:0)

Hack-R答案的略微重构版本 - 您可以为data.table添加索引:

data <- data.table(name = c("A", "B","C", "A","B","B","C","C","A"),mark = c(100,0.5,100,50,90,1000,1200,5000,210))
data[,i:=.I]

然后执行计算,但保留namei

df <- data.frame(do.call("rbind", as.list(
    by(data, data$name,
       function(x) cbind(i=x$i, 
                     name=x$name,
                     apply(x[, .(mark)], 2,function(y) {y[y > (quantile(x$mark, na.rm=TRUE)[[3]][[1]] + 1.5 * IQR(x$mark))] <- (quantile(x$mark, na.rm=TRUE)[[3]][[1]] + 1.5 * IQR(x$mark));y})
                     )))))

最后你使用索引订购:

df[order(df$i),]
  i name    mark
1 1    A     100
4 2    B     0.5
7 3    C     100
2 4    A      50
5 5    B      90
6 6    B 839.625
8 7    C    1200
9 8    C    4875
3 9    A     210