折叠行,其中一些全是 NA,另一些与一些 NA 不相交
Collapsing rows where some are all NA, others are disjoint with some NAs
我有一个简单的数据框:
ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 25 30 NA NA
2 NA NA 35 40
我想重新格式化它:
ID Col1 Col2 Col3 Col4
1 5 10 15 20
2 25 30 35 40
(请注意:真实数据集有数千行,值来自生物数据——NA
不遵循简单的模式,除了 NA
是不相交的,是的,每个 ID
).
恰好有 3 行
第一步:删除只有 NA
值的行。
表面上看起来很简单,但我 运行 遇到了一些问题。
complete.cases(DF)
returns 所有 FALSE
,所以我不能真正使用它来删除所有 NA
的行,如 DF[complete.cases(DF),]
.这是因为所有行都至少包含一个 NA
。
由于 NA
想要自我传播,其他使用 is.na
的方案也因同样的原因而失败。
第二步:将剩余的两行合二为一。
考虑使用 aggregate
之类的方法来解决这个问题,但肯定有比 this 更简单的方法,但根本行不通。
感谢任何建议。
简单的方法是:
as.data.frame(lapply(myData[,c('Col1','Col2','Col3','Col4')],function(x)[!is.na(x)]))
但如果并非所有列都具有相同数量的非 NA
值,那么您需要 trim 他们像这样:
temp <- lapply(myData[,c('Col1','Col2','Col3','Col4')],function(x)x[!is.na(x)])
len <- min(sapply(temp,length))
as.data.frame(lapply(temp,`[`,seq(len)))
这里有一些尝试的总和:
aggregate(. ~ ID, data=dat, FUN=na.omit, na.action="na.pass")
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
由于 aggregate
的公式界面在进行任何分组之前默认对整个数据使用 na.omit
,它将删除 dat
的每一行,因为它们都至少包含一个NA
值。试试看:nrow(na.omit(dat))
returns 0
。所以在这种情况下,在aggregate
中使用na.pass
,然后使用na.omit
来跳过通过的NA
。
或者,不使用公式界面并手动指定要聚合的列:
aggregate(dat[-1], dat[1], FUN=na.omit )
aggregate(dat[c("Col1","Col2","Col3","Col4")], dat["ID"], FUN=na.omit)
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
尝试
library(dplyr)
DF %>% group_by(ID) %>% summarise_each(funs(sum(., na.rm = TRUE)))
编辑:为了解决一列对于某个ID
所有NAs
的情况,我们需要sum_NA()
函数returns NA
如果全部是 NAs
txt <- "ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 NA 30 NA NA
2 NA NA 35 40"
DF <- read.table(text = txt, header = TRUE)
# original code
DF %>%
group_by(ID) %>%
summarise_each(funs(sum(., na.rm = TRUE)))
# `summarise_each()` is deprecated.
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
# To map `funs` over all variables, use `summarise_all()`
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 0 30 35 40
sum_NA <- function(x) {if (all(is.na(x))) x[NA_integer_] else sum(x, na.rm = TRUE)}
DF %>%
group_by(ID) %>%
summarise_all(funs(sum_NA))
DF %>%
group_by(ID) %>%
summarise_if(is.numeric, funs(sum_NA))
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 NA 30 35 40
这是一种数据 table 方法,它使用 na.omit()
跨列,按 ID 分组。
library(data.table)
setDT(df)[, lapply(.SD, na.omit), by = ID]
# ID Col1 Col2 Col3 Col4
# 1: 1 5 10 15 20
# 2: 2 25 30 35 40
从dplyr 1.0.0
开始,你还可以这样做(使用@Khashaa提供的数据):
df %>%
group_by(ID) %>%
summarize(across(everything(), ~ first(na.omit(.))))
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 NA 30 35 40
我有一个简单的数据框:
ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 25 30 NA NA
2 NA NA 35 40
我想重新格式化它:
ID Col1 Col2 Col3 Col4
1 5 10 15 20
2 25 30 35 40
(请注意:真实数据集有数千行,值来自生物数据——NA
不遵循简单的模式,除了 NA
是不相交的,是的,每个 ID
).
第一步:删除只有 NA
值的行。
表面上看起来很简单,但我 运行 遇到了一些问题。
complete.cases(DF)
returns 所有 FALSE
,所以我不能真正使用它来删除所有 NA
的行,如 DF[complete.cases(DF),]
.这是因为所有行都至少包含一个 NA
。
由于 NA
想要自我传播,其他使用 is.na
的方案也因同样的原因而失败。
第二步:将剩余的两行合二为一。
考虑使用 aggregate
之类的方法来解决这个问题,但肯定有比 this 更简单的方法,但根本行不通。
感谢任何建议。
简单的方法是:
as.data.frame(lapply(myData[,c('Col1','Col2','Col3','Col4')],function(x)[!is.na(x)]))
但如果并非所有列都具有相同数量的非 NA
值,那么您需要 trim 他们像这样:
temp <- lapply(myData[,c('Col1','Col2','Col3','Col4')],function(x)x[!is.na(x)])
len <- min(sapply(temp,length))
as.data.frame(lapply(temp,`[`,seq(len)))
这里有一些尝试的总和:
aggregate(. ~ ID, data=dat, FUN=na.omit, na.action="na.pass")
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
由于 aggregate
的公式界面在进行任何分组之前默认对整个数据使用 na.omit
,它将删除 dat
的每一行,因为它们都至少包含一个NA
值。试试看:nrow(na.omit(dat))
returns 0
。所以在这种情况下,在aggregate
中使用na.pass
,然后使用na.omit
来跳过通过的NA
。
或者,不使用公式界面并手动指定要聚合的列:
aggregate(dat[-1], dat[1], FUN=na.omit )
aggregate(dat[c("Col1","Col2","Col3","Col4")], dat["ID"], FUN=na.omit)
# ID Col1 Col2 Col3 Col4
#1 1 5 10 15 20
#2 2 25 30 35 40
尝试
library(dplyr)
DF %>% group_by(ID) %>% summarise_each(funs(sum(., na.rm = TRUE)))
编辑:为了解决一列对于某个ID
所有NAs
的情况,我们需要sum_NA()
函数returns NA
如果全部是 NAs
txt <- "ID Col1 Col2 Col3 Col4
1 NA NA NA NA
1 5 10 NA NA
1 NA NA 15 20
2 NA NA NA NA
2 NA 30 NA NA
2 NA NA 35 40"
DF <- read.table(text = txt, header = TRUE)
# original code
DF %>%
group_by(ID) %>%
summarise_each(funs(sum(., na.rm = TRUE)))
# `summarise_each()` is deprecated.
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
# To map `funs` over all variables, use `summarise_all()`
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 0 30 35 40
sum_NA <- function(x) {if (all(is.na(x))) x[NA_integer_] else sum(x, na.rm = TRUE)}
DF %>%
group_by(ID) %>%
summarise_all(funs(sum_NA))
DF %>%
group_by(ID) %>%
summarise_if(is.numeric, funs(sum_NA))
# A tibble: 2 x 5
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 NA 30 35 40
这是一种数据 table 方法,它使用 na.omit()
跨列,按 ID 分组。
library(data.table)
setDT(df)[, lapply(.SD, na.omit), by = ID]
# ID Col1 Col2 Col3 Col4
# 1: 1 5 10 15 20
# 2: 2 25 30 35 40
从dplyr 1.0.0
开始,你还可以这样做(使用@Khashaa提供的数据):
df %>%
group_by(ID) %>%
summarize(across(everything(), ~ first(na.omit(.))))
ID Col1 Col2 Col3 Col4
<int> <int> <int> <int> <int>
1 1 5 10 15 20
2 2 NA 30 35 40