在R中,从df中采样n行,其中某列具有非NA值(有条件采样)



背景

这是一个玩具df:

df <- data.frame(ID = c("a","b","c","d","e","f"), 
gender = c("f","f","m","f","m","m"), 
zip = c(48601,NA,29910,54220,NA,44663),stringsAsFactors=FALSE)

正如您所看到的,我在zip列中有几个NA值。

问题

我试图从df中随机抽取2整行,但我希望它们是zip不为空的行。

我尝试过的

这个代码给我一个基本的(即无条件的)随机样本:

df2 <- df[sample(nrow(df), 2), ]

当然,这只会让我达到目标的一半——很多时候它会返回一行zip中的NA值。此代码尝试添加条件:

df2 <- df[sample(nrow(df$zip != NA), 2), ]

我想我已经接近了,但这会产生一个错误invalid first argument

有什么想法吗?

我们可以使用is.na

tmp <- df[!is.na(df$zip),]
> tmp[sample(nrow(tmp), 2),]

我们可以使用rownames+na.omit对行进行采样

> df[sample(rownames(na.omit(df["zip"])), 2),]
ID gender   zip
3  c      m 29910
4  d      f 54220

这是一个带有complete.cases()的基本R解决方案

# define a logical vector to identify NA
x <- complete.cases(df)
# subset only not NA values
df_no_na <- df[x,]
# do the sample
df_no_na[sample(nrow(df_no_na), 2),]

输出:

ID gender   zip
3  c      m 29910
6  f      m 44663

对于tidyverse爱好者来说。。。

library("dplyr")
df %>% 
tidyr::drop_na() %>% 
dplyr::slice_sample(n = 2)

如果您关心的zip列中只有NA,则:

df %>% 
tidyr::drop_na(zip) %>% 
dplyr::slice_sample(n = 2)

这里重要的是避免创建不必要的第二个数据帧,同时删除NA值。您可以使用另一个答案中给出的na.omit使用该解决方案,但也可以使用which返回要采样的有效行列表。例如:

nsamp <- 23
df[sample(which(!is.na(df$zip)), nsamp), ]

这样做的好处是which内部的条件可以是任何您喜欢的条件,无论它是否包含缺失值。例如,该版本将从所有邮政编码中以336开头的女性行中进行采样:

df[sample(which(df$gender=='f' & grepl('^336', df$zip)), nsamp), ]

最新更新