如何根据特定的变量值消毒df?

时间:2017-09-07 14:45:41

标签: r dataframe extract sanitize

我有两个数据框。 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

我想获得一个新的数据框,其中同时存在XYZT主要超过特定阈值的值。

实施例。我需要同时(在同一行):

  • X, Y > 2
  • Z, T > 4

我需要使用第二个数据框来达到我的目标,我希望如下:

dfTwo$C.1>2

所以结果将是具有这种结构的新数据帧:

 X Y Z T J
 3 4 5 6 1

我怎么能这样做?

4 个答案:

答案 0 :(得分:3)

以下是包含MapReduce的基本R方法。

# 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返回长度为4的列表,其中逻辑变量对应于每个比较。此列表传递给Reduce,它返回一个逻辑向量,其长度对应于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))

答案 1 :(得分: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

答案 2 :(得分:1)

我们可以使用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中的相应值,或valsNA,则输出应为该列中的原始值。否则,该值将替换为NAmap2_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

要完成此任务,我会加载dplyrtidyr包。

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

第二步是添加阈值信息。一种方法是创建一个查找表,显示GroupValue之间的关联

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。我们可以使用complete中的tidyr函数。以下代码通过在Col中添加dat但在dfTwo3NA中添加到值来完成数据框。

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

我们快到了。现在我们可以从vals创建dfTwo5

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

答案 3 :(得分:0)

我写的是假设第二个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)

如果您只需要这些,我会在查看问题的更复杂版本时保存此评论。