根据具有较少行的级别将每个级别的n行子集

时间:2018-11-01 13:14:31

标签: r

想象一下我有一些具有一定数量级别的数据框:

    x1    x2  ...  xi   Level
1    1     1        1       A    
2    2     2        4       A
3    1     4        2       B
.    .     .        .       B 
.    .     .        .       B
.    .     .        .       C
.    .     .        .       C
.    .     .        .       C

我正在尝试以所有级别具有n行的方式对数据帧进行子集化,其中n是具有较少实例的级别的行数。在上面的示例中,A是人口较少的级别(2行),因此所需的输出是:

    x1    x2  ...  xi   Level
1    1     1        1       A    
2    2     2        4       A
3    1     4        2       B
.    .     .        .       B
.    .     .        .       C
.    .     .        .       C

级别和行的数量是可变的,因此有必要每次检查具有较少行的级别。此外,例如,如果我有以下条件,则需要以(伪)随机方式选择每个级别的n行:

    x1    x2  ...  xi   Level
1    1     1        1       A    
.    .     .        .       .
.    .     .        .       .
.    .     .        .       .
10   1     2        3       C  
11   3     2        1       C  
12   2     1        3       C  
13   3     1        2       C  
14   2     3        1       C

并且n = 3,我想避免选择C级的前三行(10,11,12)。 预先感谢。

4 个答案:

答案 0 :(得分:2)

以下解决方案仅使用基数R。

n <- min(tapply(Level, Level, length))
inx <- unlist(tapply(seq_along(Level), Level, FUN = function(x) sample(x, n)))
dat[inx, ]
#              x Level
#2   0.414641434     A
#3  -1.539950042     A
#5  -0.294720447     B
#6  -0.005767173     B
#9  -0.799009249     C
#8   0.763593461     C
#14  0.252223448     D
#11 -0.289461574     D
#16  0.435683299     E
#17 -1.237538422     E

我已经留下了这样的行名,以表明它们不是连续选择的。如果以后需要连续的行名,请

sel <- dat[inx, ]
row.names(sel) <- NULL

数据。

set.seed(1)
s <- sample(2:5, 5, TRUE)
Level <- sapply(1:5, function(i) rep(LETTERS[i], each = s[i]))
Level <- factor(unlist(Level))
dat <- data.frame(x = rnorm(length(Level)), Level)

答案 1 :(得分:2)

data.table解决方案。 dat[, .N, Level]给出每个Levels组的行数,然后min(N),即minN是最小的行数。下一行是每个组的minN行中的前排。

library(data.table)
setDT(dat)

minN <- dat[, .N, Level][, min(N)]
dat[, head(.SD, minN), Level]

#     Level          x
#  1:     A  1.2724293
#  2:     A  0.4146414
#  3:     B -0.9285670
#  4:     B -0.2947204
#  5:     C  2.4046534
#  6:     C  0.7635935
#  7:     D -0.2894616
#  8:     D -0.2992151
#  9:     E  0.4356833
# 10:     E -1.2375384

如果要选择随机行,可以混合使用dplyrdata.table

library(dplyr) 

dat[, sample_n(.SD, minN), Level]

或者在arg0naut的评论中使用解决方案

dat[, .SD[sample(.N, minN)], by = Level]

如果您愿意为了速度而牺牲一些可读性,那么另一个选择是

dat[dat[, sample(.I, minN), Level]$V1]

使用的数据(来自Rui Barradas的回答)

set.seed(1)
s <- sample(2:5, 5, TRUE)
Level <- sapply(1:5, function(i) rep(LETTERS[i], each = s[i]))
Level <- factor(unlist(Level))
dat <- data.frame(x = rnorm(length(Level)), Level)

答案 2 :(得分:1)

这是一个dplyr解决方案:

library(dplyr)
df %>% group_by(Level) %>%  ## group by level
  mutate(count = n()) %>% ## count number of rows for each group
  ungroup() %>%          
  mutate(count = min(count)) %>%  ## select the minimal number of rows 
  group_by(Level) %>%             ## group again to get X rows for each group
  slice(sample(1:n(), min(count))) %>%    ## get the  X random rows
  ungroup() %>% 
  select(-count)             ## remove the added count variable

答案 3 :(得分:1)

使用dplyr进行随机化的sample_frac方法:

library(dplyr)

df %>%
  add_count(Level) %>%
  mutate(
    n = min(n)
  ) %>%
  group_by(Level) %>%
  sample_frac(1) %>%
  slice(1:n) %>%
  select(-n)