如何根据特定变量值清理 df?
How to sanitize a df according to specific variable values?
我有两个数据框。 dfOne
是这样制作的:
X Y Z T J
3 4 5 6 1
1 2 3 4 1
5 1 2 5 1
和dfTwo
是这样制作的
C.1 C.2
X Z
Y T
我想获得一个新的数据帧,其中同时存在 X
、Y
、Z
、T
大于特定阈值的值。
例子。我需要同时(在同一行):
X, Y > 2
Z, T > 4
我需要使用第二个数据帧来到达我的 objective,我希望是这样的:
dfTwo$C.1>2
因此结果将是具有以下结构的新数据框:
X Y Z T J
3 4 5 6 1
我该怎么做?
dfOne[Reduce(intersect, list(which(dfOne["X"] > 2),
which(dfOne["Y"] > 2),
which(dfOne["Z"] > 4),
which(dfOne["T"] > 4))),]
# X Y Z T J
#1 3 4 5 6 1
或迭代(因此测试的不等式更少):
vals = c(X = 2, Y = 2, Z = 4, T = 4) # from @lmo's answer
dfOne[Reduce(intersect, lapply(names(vals), function(x) which(dfOne[x] > vals[x]))),]
# X Y Z T J
#1 3 4 5 6 1
我写这篇文章时假设第二个 DF 旨在对第一个 DF 中的字段进行分类。如果你不需要使用第二个来定义条件,那就更简单了:
dfNew = dfOne[dfOne$X > 2 & dfOne$Y > 2 & dfOne$Z > 4 & dfOne$T > 4, ]
或者,使用 dplyr:
library(dplyr)
dfNew = dfOne %>% filter(X > 2 & Y > 2 & Z > 4 & T > 4)
如果这就是您所需要的,我将在查看问题的更复杂版本时保存此评论。
这是一个基本的 R 方法 Map
和 Reduce
。
# build lookup table of thresholds relative to variable name
vals <- setNames(c(2, 2, 4, 4), unlist(dat2))
# subset data.frame
dat[Reduce("&", Map(">", dat[names(vals)], vals)), ]
X Y Z T J
1 3 4 5 6 1
这里,Map
returns一个长度为4的列表,每个比较对应一个逻辑变量。此列表传递给 Reduce
,其中 returns 是一个长度与 data.frame 中的行数相对应的单个逻辑向量,dat。此逻辑向量用于子集数据。
数据
dat <-
structure(list(X = c(3L, 1L, 5L), Y = c(4L, 2L, 1L), Z = c(5L,
3L, 2L), T = c(6L, 4L, 5L), J = c(1L, 1L, 1L)), .Names = c("X",
"Y", "Z", "T", "J"), class = "data.frame", row.names = c(NA,
-3L))
dat2 <-
structure(list(C.1 = structure(1:2, .Label = c("X", "Y"), class = "factor"),
C.2 = structure(c(2L, 1L), .Label = c("T", "Z"), class = "factor")), .Names = c("C.1",
"C.2"), class = "data.frame", row.names = c(NA, -2L))
我们可以使用purrr
包
这是输入数据。
# Data frame from lmo's solution
dat <-
structure(list(X = c(3L, 1L, 5L), Y = c(4L, 2L, 1L), Z = c(5L,
3L, 2L), T = c(6L, 4L, 5L), J = c(1L, 1L, 1L)), .Names = c("X",
"Y", "Z", "T", "J"), class = "data.frame", row.names = c(NA,
-3L))
# A numeric vector to show the threshold values
# Notice that columns without any requirements need NA
vals <- c(X = 2, Y = 2, Z = 4, T = 4, J = NA)
这是实现
library(purrr)
map2_dfc(dat, vals, ~ifelse(.x > .y | is.na(.y), .x, NA)) %>% na.omit()
# A tibble: 1 x 5
X Y Z T J
<int> <int> <int> <int> <int>
1 3 4 5 6 1
map2_dfc
使用定义的函数逐一循环遍历 dat
中的每一列和 vals
中的每个值。 ~ifelse(.x > .y | is.na(.y), .x, NA)
表示如果每列中的数字大于 vals
中对应的值,或者 vals
是 NA
,则输出应为该列的原始值。否则,该值将替换为 NA
。 map2_dfc(dat, vals, ~ifelse(.x > .y | is.na(.y), .x, NA))
的输出是一个数据框,在某些行中有 NA
值,表明不满足条件。最后,na.omit
删除了那些行。
更新
在这里,我演示了如何在我的示例中将 dfTwo
数据帧转换为 vals
向量。
首先,让我们创建 dfTwo
数据框。
dfTwo <- read.table(text = "C.1 C.2
X Z
Y T",
header = TRUE, stringsAsFactors = FALSE)
dfTwo
C.1 C.2
1 X Z
2 Y T
为了完成任务,我加载了 dplyr
和 tidyr
包。
library(dplyr)
library(tidyr)
现在我开始改造dfTwo
。第一步是使用stack
函数转换格式。
dfTwo2 <- dfTwo %>%
stack() %>%
setNames(c("Col", "Group")) %>%
mutate(Group = as.character(Group))
dfTwo2
Col Group
1 X C.1
2 Y C.1
3 Z C.2
4 T C.2
第二步,添加阈值信息。一种方法是创建一个查找 table,显示 Group
和 Value
之间的关联
threshold_df <- data.frame(Group = c("C.1", "C.2"),
Value = c(2, 4),
stringsAsFactors = FALSE)
threshold_df
Group Value
1 C.1 2
2 C.2 4
然后我们可以使用left_join
函数来合并数据框。
dfTwo3 <- dfTwo2 %>% left_join(threshold_dt, by = "Group")
dfTwo3
Col Group Value
1 X C.1 2
2 Y C.1 2
3 Z C.2 4
4 T C.2 4
现在是第三步。请注意,有一个名为 J
的列不需要任何阈值。所以我们需要将这些信息添加到dfTwo3
。我们可以使用 tidyr
中的 complete
函数。以下代码通过在 dat
中添加 Col
而不是在 dfTwo3
和 NA
中添加到值来完成数据框。
dfTwo4 <- dfTwo3 %>% complete(Col = colnames(dat))
dfTwo4
# A tibble: 5 x 3
Col Group Value
<chr> <chr> <dbl>
1 J <NA> NA
2 T C.2 4
3 X C.1 2
4 Y C.1 2
5 Z C.2 4
第四步是dfTwo4
的正确排序。我们可以通过将 Col
转换为因子并根据 dat
.
中列名的顺序分配级别来实现这一点
dfTwo5 <- dfTwo4 %>%
mutate(Col = factor(Col, levels = colnames(dat))) %>%
arrange(Col) %>%
mutate(Col = as.character(Col))
dfTwo5
# A tibble: 5 x 3
Col Group Value
<chr> <chr> <dbl>
1 X C.1 2
2 Y C.1 2
3 Z C.2 4
4 T C.2 4
5 J <NA> NA
我们快到了。现在我们可以从 dfTwo5
.
创建 vals
vals <- dfTwo5$Value
names(vals) <- dfTwo5$Col
vals
X Y Z T J
2 2 4 4 NA
现在我们准备使用 purrr
包来过滤数据。
以上是步骤分解。为了简单起见,我们可以将所有这些步骤组合成以下代码。
library(dplyr)
library(tidyr)
threshold_df <- data.frame(Group = c("C.1", "C.2"),
Value = c(2, 4),
stringsAsFactors = FALSE)
dfTwo2 <- dfTwo %>%
stack() %>%
setNames(c("Col", "Group")) %>%
mutate(Group = as.character(Group)) %>%
left_join(threshold_df, by = "Group") %>%
complete(Col = colnames(dat)) %>%
mutate(Col = factor(Col, levels = colnames(dat))) %>%
arrange(Col) %>%
mutate(Col = as.character(Col))
vals <- dfTwo2$Value
names(vals) <- dfTwo2$Col
我有两个数据框。 dfOne
是这样制作的:
X Y Z T J
3 4 5 6 1
1 2 3 4 1
5 1 2 5 1
和dfTwo
是这样制作的
C.1 C.2
X Z
Y T
我想获得一个新的数据帧,其中同时存在 X
、Y
、Z
、T
大于特定阈值的值。
例子。我需要同时(在同一行):
X, Y > 2
Z, T > 4
我需要使用第二个数据帧来到达我的 objective,我希望是这样的:
dfTwo$C.1>2
因此结果将是具有以下结构的新数据框:
X Y Z T J
3 4 5 6 1
我该怎么做?
dfOne[Reduce(intersect, list(which(dfOne["X"] > 2),
which(dfOne["Y"] > 2),
which(dfOne["Z"] > 4),
which(dfOne["T"] > 4))),]
# X Y Z T J
#1 3 4 5 6 1
或迭代(因此测试的不等式更少):
vals = c(X = 2, Y = 2, Z = 4, T = 4) # from @lmo's answer
dfOne[Reduce(intersect, lapply(names(vals), function(x) which(dfOne[x] > vals[x]))),]
# X Y Z T J
#1 3 4 5 6 1
我写这篇文章时假设第二个 DF 旨在对第一个 DF 中的字段进行分类。如果你不需要使用第二个来定义条件,那就更简单了:
dfNew = dfOne[dfOne$X > 2 & dfOne$Y > 2 & dfOne$Z > 4 & dfOne$T > 4, ]
或者,使用 dplyr:
library(dplyr)
dfNew = dfOne %>% filter(X > 2 & Y > 2 & Z > 4 & T > 4)
如果这就是您所需要的,我将在查看问题的更复杂版本时保存此评论。
这是一个基本的 R 方法 Map
和 Reduce
。
# build lookup table of thresholds relative to variable name
vals <- setNames(c(2, 2, 4, 4), unlist(dat2))
# subset data.frame
dat[Reduce("&", Map(">", dat[names(vals)], vals)), ]
X Y Z T J
1 3 4 5 6 1
这里,Map
returns一个长度为4的列表,每个比较对应一个逻辑变量。此列表传递给 Reduce
,其中 returns 是一个长度与 data.frame 中的行数相对应的单个逻辑向量,dat。此逻辑向量用于子集数据。
数据
dat <-
structure(list(X = c(3L, 1L, 5L), Y = c(4L, 2L, 1L), Z = c(5L,
3L, 2L), T = c(6L, 4L, 5L), J = c(1L, 1L, 1L)), .Names = c("X",
"Y", "Z", "T", "J"), class = "data.frame", row.names = c(NA,
-3L))
dat2 <-
structure(list(C.1 = structure(1:2, .Label = c("X", "Y"), class = "factor"),
C.2 = structure(c(2L, 1L), .Label = c("T", "Z"), class = "factor")), .Names = c("C.1",
"C.2"), class = "data.frame", row.names = c(NA, -2L))
我们可以使用purrr
包
这是输入数据。
# Data frame from lmo's solution
dat <-
structure(list(X = c(3L, 1L, 5L), Y = c(4L, 2L, 1L), Z = c(5L,
3L, 2L), T = c(6L, 4L, 5L), J = c(1L, 1L, 1L)), .Names = c("X",
"Y", "Z", "T", "J"), class = "data.frame", row.names = c(NA,
-3L))
# A numeric vector to show the threshold values
# Notice that columns without any requirements need NA
vals <- c(X = 2, Y = 2, Z = 4, T = 4, J = NA)
这是实现
library(purrr)
map2_dfc(dat, vals, ~ifelse(.x > .y | is.na(.y), .x, NA)) %>% na.omit()
# A tibble: 1 x 5
X Y Z T J
<int> <int> <int> <int> <int>
1 3 4 5 6 1
map2_dfc
使用定义的函数逐一循环遍历 dat
中的每一列和 vals
中的每个值。 ~ifelse(.x > .y | is.na(.y), .x, NA)
表示如果每列中的数字大于 vals
中对应的值,或者 vals
是 NA
,则输出应为该列的原始值。否则,该值将替换为 NA
。 map2_dfc(dat, vals, ~ifelse(.x > .y | is.na(.y), .x, NA))
的输出是一个数据框,在某些行中有 NA
值,表明不满足条件。最后,na.omit
删除了那些行。
更新
在这里,我演示了如何在我的示例中将 dfTwo
数据帧转换为 vals
向量。
首先,让我们创建 dfTwo
数据框。
dfTwo <- read.table(text = "C.1 C.2
X Z
Y T",
header = TRUE, stringsAsFactors = FALSE)
dfTwo
C.1 C.2
1 X Z
2 Y T
为了完成任务,我加载了 dplyr
和 tidyr
包。
library(dplyr)
library(tidyr)
现在我开始改造dfTwo
。第一步是使用stack
函数转换格式。
dfTwo2 <- dfTwo %>%
stack() %>%
setNames(c("Col", "Group")) %>%
mutate(Group = as.character(Group))
dfTwo2
Col Group
1 X C.1
2 Y C.1
3 Z C.2
4 T C.2
第二步,添加阈值信息。一种方法是创建一个查找 table,显示 Group
和 Value
threshold_df <- data.frame(Group = c("C.1", "C.2"),
Value = c(2, 4),
stringsAsFactors = FALSE)
threshold_df
Group Value
1 C.1 2
2 C.2 4
然后我们可以使用left_join
函数来合并数据框。
dfTwo3 <- dfTwo2 %>% left_join(threshold_dt, by = "Group")
dfTwo3
Col Group Value
1 X C.1 2
2 Y C.1 2
3 Z C.2 4
4 T C.2 4
现在是第三步。请注意,有一个名为 J
的列不需要任何阈值。所以我们需要将这些信息添加到dfTwo3
。我们可以使用 tidyr
中的 complete
函数。以下代码通过在 dat
中添加 Col
而不是在 dfTwo3
和 NA
中添加到值来完成数据框。
dfTwo4 <- dfTwo3 %>% complete(Col = colnames(dat))
dfTwo4
# A tibble: 5 x 3
Col Group Value
<chr> <chr> <dbl>
1 J <NA> NA
2 T C.2 4
3 X C.1 2
4 Y C.1 2
5 Z C.2 4
第四步是dfTwo4
的正确排序。我们可以通过将 Col
转换为因子并根据 dat
.
dfTwo5 <- dfTwo4 %>%
mutate(Col = factor(Col, levels = colnames(dat))) %>%
arrange(Col) %>%
mutate(Col = as.character(Col))
dfTwo5
# A tibble: 5 x 3
Col Group Value
<chr> <chr> <dbl>
1 X C.1 2
2 Y C.1 2
3 Z C.2 4
4 T C.2 4
5 J <NA> NA
我们快到了。现在我们可以从 dfTwo5
.
vals
vals <- dfTwo5$Value
names(vals) <- dfTwo5$Col
vals
X Y Z T J
2 2 4 4 NA
现在我们准备使用 purrr
包来过滤数据。
以上是步骤分解。为了简单起见,我们可以将所有这些步骤组合成以下代码。
library(dplyr)
library(tidyr)
threshold_df <- data.frame(Group = c("C.1", "C.2"),
Value = c(2, 4),
stringsAsFactors = FALSE)
dfTwo2 <- dfTwo %>%
stack() %>%
setNames(c("Col", "Group")) %>%
mutate(Group = as.character(Group)) %>%
left_join(threshold_df, by = "Group") %>%
complete(Col = colnames(dat)) %>%
mutate(Col = factor(Col, levels = colnames(dat))) %>%
arrange(Col) %>%
mutate(Col = as.character(Col))
vals <- dfTwo2$Value
names(vals) <- dfTwo2$Col