如何通过sorting数据框中的列来快速形成组(四分位数,十进制数等)

我看到许多问题和答案重新ordersort 。 是否有向量或数据框分组(如四分位数或十分位数)? 我有一个“手动”的解决scheme,但可能有一个更好的解决scheme已经过集体testing。

这是我的尝试:

 temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12)) temp # name value quartile # 1 a 2.55118169 NA # 2 b 0.79755259 NA # 3 c 0.16918905 NA # 4 d 1.73359245 NA # 5 e 0.41027113 NA # 6 f 0.73012966 NA # 7 g -1.35901658 NA # 8 h -0.80591167 NA # 9 i 0.48966739 NA # 10 j 0.88856758 NA # 11 k 0.05146856 NA # 12 l -0.12310229 NA temp.sorted <- temp[order(temp$value), ] temp.sorted$quartile <- rep(1:4, each=12/4) temp <- temp.sorted[order(as.numeric(rownames(temp.sorted))), ] temp # name value quartile # 1 a 2.55118169 4 # 2 b 0.79755259 3 # 3 c 0.16918905 2 # 4 d 1.73359245 4 # 5 e 0.41027113 2 # 6 f 0.73012966 3 # 7 g -1.35901658 1 # 8 h -0.80591167 1 # 9 i 0.48966739 3 # 10 j 0.88856758 4 # 11 k 0.05146856 2 # 12 l -0.12310229 1 

有更好的(更清洁/更快/单线)方法吗? 谢谢!

我使用的方法是其中之一或Hmisc::cut2(value, g=4)

 temp$quartile <- with(temp, cut(value, breaks=quantile(value, probs=seq(0,1, by=0.25), na.rm=TRUE), include.lowest=TRUE)) 

替代可能是:

 temp$quartile <- with(temp, factor( findInterval( val, c(-Inf, quantile(val, probs=c(0.25, .5, .75)), Inf) , na.rm=TRUE), labels=c("Q1","Q2","Q3","Q4") )) 

第一个是把价值观标上四分位数的副作用,我认为这是一个“好事”,但如果它不是“对你有好处”,或者在评论中提出的有效问题是一个问题,你可以去与版本2.您可以使用labels= cut ,或者您可以添加此行到您的代码:

 temp$quartile <- factor(temp$quartile, levels=c("1","2","3","4") ) 

或者甚至更快,但稍微模糊一些,虽然它不再是一个因素,而是一个数字vector:

 temp$quartile <- as.numeric(temp$quartile) 

dplyr有一个方便的函数。 它非常灵活,您可以非常容易地定义要创build的* tile或“bin”的数量。

加载包(如果没有的话先安装)并添加四分位数列:

 library(dplyr) temp$quartile <- ntile(temp$value, 4) 

或者,如果你想使用dplyr语法:

 temp <- temp %>% mutate(quartile = ntile(value, 4)) 

结果在这两种情况下是:

 temp # name value quartile #1 a -0.56047565 1 #2 b -0.23017749 2 #3 c 1.55870831 4 #4 d 0.07050839 2 #5 e 0.12928774 3 #6 f 1.71506499 4 #7 g 0.46091621 3 #8 h -1.26506123 1 #9 i -0.68685285 1 #10 j -0.44566197 2 #11 k 1.22408180 4 #12 l 0.35981383 3 

数据:

请注意,您不需要事先创build“四分位数”列,并使用set.seed使随机数可重现:

 set.seed(123) temp <- data.frame(name=letters[1:12], value=rnorm(12)) 

我会添加data.table版本的其他人谷歌search(即,@ BondedDust的解决scheme转换为data.table ):

 library(data.table) setDT(temp) temp[ , quartile := cut(value, breaks = quantile(value, probs = seq(0, 1, by = 1/4)), labels = 1:4, right = FALSE)] 

哪一个比我以前做的更好(更干净, 更快 )

 temp[ , quartile := as.factor(ifelse(value < quantile(value, .25), 1, ifelse(value < quantile(value, .5), 2, ifelse(value < quantile(value, .75), 3, 4))] 

您可以使用quantile()函数,但是在使用cut()时需要处理舍入/精度。 所以

 set.seed(123) temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12)) brks <- with(temp, quantile(value, probs = c(0, 0.25, 0.5, 0.75, 1))) temp <- within(temp, quartile <- cut(value, breaks = brks, labels = 1:4, include.lowest = TRUE)) 

赠送:

 > head(temp) name value quartile 1 a -0.56047565 1 2 b -0.23017749 2 3 c 1.55870831 4 4 d 0.07050839 2 5 e 0.12928774 3 6 f 1.71506499 4 

对不起,晚了一点晚了。 我想用cut2添加我的一个class轮,因为我不知道我的数据的最大/最小值,并希望这些组相同的大小。 我在一个被标记为重复的问题上阅读了cut2(链接在下面)。

 library(Hmisc) #For cut2 set.seed(123) #To keep answers below identical to my random run temp <- data.frame(name=letters[1:12], value=rnorm(12), quartile=rep(NA, 12)) temp$quartile <- as.numeric(cut2(temp$value, g=4)) #as.numeric to number the factors temp$quartileBounds <- cut2(temp$value, g=4) temp 

结果:

 > temp name value quartile quartileBounds 1 a -0.56047565 1 [-1.265,-0.446) 2 b -0.23017749 2 [-0.446, 0.129) 3 c 1.55870831 4 [ 1.224, 1.715] 4 d 0.07050839 2 [-0.446, 0.129) 5 e 0.12928774 3 [ 0.129, 1.224) 6 f 1.71506499 4 [ 1.224, 1.715] 7 g 0.46091621 3 [ 0.129, 1.224) 8 h -1.26506123 1 [-1.265,-0.446) 9 i -0.68685285 1 [-1.265,-0.446) 10 j -0.44566197 2 [-0.446, 0.129) 11 k 1.22408180 4 [ 1.224, 1.715] 12 l 0.35981383 3 [ 0.129, 1.224) 

类似的问题,我详细阅读有关cut2

调整dplyr::ntile以利用data.table优化提供了一个更快的解决scheme。

 library(data.table) setDT(temp) temp[order(value) , quartile := floor( 1 + 4 * (.I-1) / .N)] 

可能不合格,但更快,更简单。

定时更大的数据集

比较这个解决scheme的ntilecut data.table由@docendo_discimus和@MichaelChiricobuild议。

 library(microbenchmark) library(dplyr) set.seed(123) n <- 1e6 temp <- data.frame(name=sample(letters, size=n, replace=TRUE), value=rnorm(n)) setDT(temp) microbenchmark( "ntile" = temp[, quartile_ntile := ntile(value, 4)], "cut" = temp[, quartile_cut := cut(value, breaks = quantile(value, probs = seq(0, 1, by=1/4)), labels = 1:4, right=FALSE)], "dt_ntile" = temp[order(value), quartile_ntile_dt := floor( 1 + 4 * (.I-1)/.N)] ) 

得到:

 Unit: milliseconds expr min lq mean median uq max neval ntile 608.1126 647.4994 670.3160 686.5103 691.4846 712.4267 100 cut 369.5391 373.3457 375.0913 374.3107 376.5512 385.8142 100 dt_ntile 117.5736 119.5802 124.5397 120.5043 124.5902 145.7894 100 
 temp$quartile <- ceiling(sapply(temp$value,function(x) sum(x-temp$value>=0))/(length(temp$value)/4)) 

我想提出一个版本,这似乎是更强大的,因为我遇到了很多问题,使用我的数据集中的break选项cut()中的quantile() 。 我正在使用ntile函数,但它也可以用ecdf作为input。

 temp[, `:=`(quartile = .bincode(x = ntile(value, 100), breaks = seq(0,100,25), right = TRUE, include.lowest = TRUE) decile = .bincode(x = ntile(value, 100), breaks = seq(0,100,10), right = TRUE, include.lowest = TRUE) )] temp[, `:=`(quartile = .bincode(x = ecdf(value)(value), breaks = seq(0,1,0.25), right = TRUE, include.lowest = TRUE) decile = .bincode(x = ecdf(value)(value), breaks = seq(0,1,0.1), right = TRUE, include.lowest = TRUE) )] 

那是对的吗?

有可能是一个更快的方法,但我会做的:

 a <- rnorm(100) # Our data q <- quantile(a) # You can supply your own breaks, see ?quantile # Define a simple function that checks in which quantile a number falls getQuant <- function(x) { for (i in 1:(length(q)-1)) { if (x>=q[i] && x<q[i+1]) break; } i } # Apply the function to the data res <- unlist(lapply(as.matrix(a), getQuant))