R:计算每列中的出现次数并将该列的值替换为计数 (SQL?)
R: Counting occurrences in each column and replacing that column's value with the count (SQL?)
这里是原始数据的例子:
ID Test1 Test2 Test3 Test4
1 0 0 NA 1.2
1 0 NA NA 3.0
1 NA NA NA 0
2 0 0 0 0
2 0 0 NA NA
我想计算每个 ID 的非 NA 出现次数(包括 0),并将该列值替换为该数字。制作这个:
ID Test1 Test2 Test3 Test4
1 2 1 NA 3
2 2 2 1 1
我不知道是否需要在 R 中使用 sqldf 包。我尝试将数据框强制转换为数据 table 并对其进行整形,但没有成功。
df <- x %>% 融化 (idvars='ID')
感谢您的帮助。
我们可以通过sum
对逻辑向量
进行分组
library(dplyr)
df1 %>%
group_by(ID) %>%
summarise_all(funs(na_if(sum(!is.na(.)), 0)))
# A tibble: 2 x 5
# ID Test1 Test2 Test3 Test4
# <int> <int> <int> <int> <int>
#1 1 2 1 NA 3
#2 2 2 2 1 1
或使用 base R
中的 aggregate
aggregate(.~ ID, df1, FUN = function(x) sum(!is.na(x)), na.action = NULL)
或 rowsum
rowsum(+(!is.na(df1[-1])), df1$ID)
数据
df1 <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L), Test1 = c(0L, 0L,
NA, 0L, 0L), Test2 = c(0L, NA, NA, 0L, 0L), Test3 = c(NA, NA,
NA, 0L, NA), Test4 = c(1.2, 3, 0, 0, NA)), class = "data.frame",
row.names = c(NA, -5L))
下面我们讨论使用问题中提到的两个包的解决方案。
1) sqldf 要使用问题中引用的 sqldf 包,请使用末尾注释中可重复定义的输入:
library(sqldf)
sqldf("select ID,
nullif(count(Test1), 0) Test1,
nullif(count(Test2), 0) Test2,
nullif(count(Test3), 0) Test3,
nullif(count(Test4), 0) Test4
from DF
group by ID")
给予:
ID Test1 Test2 Test3 Test4
1 1 2 1 NA 3
2 2 2 2 1 1
nullif(count(test1), 0)
可以缩短为 count(test1)
如果可以为全为 NA 的 ID 报告 0 并且对于其他 test* 列也类似。
1a) 如果实际上有很多列,而不仅仅是 4 列,或者您不喜欢重复 select
的一部分,我们可以构建字符串然后像这样插入:
testNames <- names(DF)[-1]
select <- toString(sprintf("nullif(count(%s), 0) %s", testNames, testNames))
library(sqldf)
fn$sqldf("select ID, $select
from DF
group by ID")
将verbose = TRUE
参数添加到sqldf
调用以查看实际上将相同的字符串发送到后端。
如果可以报告 0 而不是 NA,那么我们可以将 select <- ...
简化为:
select <- toString(sprintf("count(%s) %s", testNames, testNames))
2) reshape2 在问题的代码尝试中使用 melt
:
library(magrittr)
library(reshape2)
count <- function(x) if (all(is.na(x))) NA_integer_ else sum(!is.na(x))
DF %>%
melt(id.vars = "ID") %>%
dcast(ID ~ variable, count)
如果可以为任何全为 NA 的 ID 报告 0,则计数可以简化为:
count <- function(x) sum(!is.na(x))
备注
Lines <- "ID Test1 Test2 Test3 Test4
1 0 0 NA 1.2
1 0 NA NA 3.0
1 NA NA NA 0
2 0 0 0 0
2 0 0 NA NA"
DF <- read.table(text = Lines, header = TRUE)
这里是原始数据的例子:
ID Test1 Test2 Test3 Test4
1 0 0 NA 1.2
1 0 NA NA 3.0
1 NA NA NA 0
2 0 0 0 0
2 0 0 NA NA
我想计算每个 ID 的非 NA 出现次数(包括 0),并将该列值替换为该数字。制作这个:
ID Test1 Test2 Test3 Test4
1 2 1 NA 3
2 2 2 1 1
我不知道是否需要在 R 中使用 sqldf 包。我尝试将数据框强制转换为数据 table 并对其进行整形,但没有成功。
df <- x %>% 融化 (idvars='ID')
感谢您的帮助。
我们可以通过sum
对逻辑向量
library(dplyr)
df1 %>%
group_by(ID) %>%
summarise_all(funs(na_if(sum(!is.na(.)), 0)))
# A tibble: 2 x 5
# ID Test1 Test2 Test3 Test4
# <int> <int> <int> <int> <int>
#1 1 2 1 NA 3
#2 2 2 2 1 1
或使用 base R
aggregate
aggregate(.~ ID, df1, FUN = function(x) sum(!is.na(x)), na.action = NULL)
或 rowsum
rowsum(+(!is.na(df1[-1])), df1$ID)
数据
df1 <- structure(list(ID = c(1L, 1L, 1L, 2L, 2L), Test1 = c(0L, 0L,
NA, 0L, 0L), Test2 = c(0L, NA, NA, 0L, 0L), Test3 = c(NA, NA,
NA, 0L, NA), Test4 = c(1.2, 3, 0, 0, NA)), class = "data.frame",
row.names = c(NA, -5L))
下面我们讨论使用问题中提到的两个包的解决方案。
1) sqldf 要使用问题中引用的 sqldf 包,请使用末尾注释中可重复定义的输入:
library(sqldf)
sqldf("select ID,
nullif(count(Test1), 0) Test1,
nullif(count(Test2), 0) Test2,
nullif(count(Test3), 0) Test3,
nullif(count(Test4), 0) Test4
from DF
group by ID")
给予:
ID Test1 Test2 Test3 Test4
1 1 2 1 NA 3
2 2 2 2 1 1
nullif(count(test1), 0)
可以缩短为 count(test1)
如果可以为全为 NA 的 ID 报告 0 并且对于其他 test* 列也类似。
1a) 如果实际上有很多列,而不仅仅是 4 列,或者您不喜欢重复 select
的一部分,我们可以构建字符串然后像这样插入:
testNames <- names(DF)[-1]
select <- toString(sprintf("nullif(count(%s), 0) %s", testNames, testNames))
library(sqldf)
fn$sqldf("select ID, $select
from DF
group by ID")
将verbose = TRUE
参数添加到sqldf
调用以查看实际上将相同的字符串发送到后端。
如果可以报告 0 而不是 NA,那么我们可以将 select <- ...
简化为:
select <- toString(sprintf("count(%s) %s", testNames, testNames))
2) reshape2 在问题的代码尝试中使用 melt
:
library(magrittr)
library(reshape2)
count <- function(x) if (all(is.na(x))) NA_integer_ else sum(!is.na(x))
DF %>%
melt(id.vars = "ID") %>%
dcast(ID ~ variable, count)
如果可以为任何全为 NA 的 ID 报告 0,则计数可以简化为:
count <- function(x) sum(!is.na(x))
备注
Lines <- "ID Test1 Test2 Test3 Test4
1 0 0 NA 1.2
1 0 NA NA 3.0
1 NA NA NA 0
2 0 0 0 0
2 0 0 NA NA"
DF <- read.table(text = Lines, header = TRUE)