【问题标题】:How to maintain the order of elements of a row when using by and rbind function in r?在r中使用by和rbind函数时如何保持一行元素的顺序?
【发布时间】:2016-10-26 11:37:55
【问题描述】:

我编写了一个函数,它根据 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

在上面的结果中,标记列的值的顺序发生了变化。有什么方法可以保持元素的顺序吗?

【问题讨论】:

  • 为什么需要apply

标签: r function sorting outliers rbind


【解决方案1】:

您确定代码正在执行您认为的操作吗?

您似乎正在用中位数 + 1.5*IQR 替换任何大于中位数(quantile 的第三个返回值)的值。也许这就是你的意图,我不知道。更大的问题是您在 apply 函数中执行此操作,因此每次迭代都会重新计算中位数和 IQR,并使用已更改的先前行进行更新。我敢打赌这不是你想要的,但我想我见过陌生人。

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

读取您的数据(为什么是“----”?)

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

如果我们用新函数改变它,我们会在没有改变行的情况下取回数据

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

最重要的是,如果您现在想要按组进行分位数比较(例如,通过 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

【讨论】:

    【解决方案2】:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-25
      相关资源
      最近更新 更多