【问题标题】:Look up (and Replace) values in data frame based on lookup table (with more than one column)根据查找表(多列)在数据框中查找(和替换)值
【发布时间】:2013-05-20 20:32:09
【问题描述】:

下面更新

:原创

我正在尝试根据匹配来自另一个数据帧的两列来找到最优雅(简单和简洁)的方法来替换某些列的值。

这是包含我希望替换的列的表(基于它们包含的值)。

> cost.table
  Identifier Phase.0.Difficulty Phase.1.Complexity Phase.2.Complexity Phase.3.Complexity Phase.4.Complexity Phase.5.Complexity
1        FS1                Low                Low                Low             Medium             Medium               High
2        FS2               High               High               High             Medium             Medium             Medium
3        FS3               High                Low                Low               High               High               High
4        FS4               High             Medium             Medium             Medium             Medium             Medium
5        FS5               High             Medium             Medium               High             Medium             Medium
  Phase.6.Complexity Transaction.Feasibility Approach
1               High                  Medium        B
2             Medium                  Medium        I
3               High                  Medium        B
4             Medium                  Medium        I
5             Medium                  Medium        B

这里是我希望用来查找正确替换值的查找表。

> cost.approach.difficulty
  Approach Difficulty   Phase 0  Phase 1  Phase 2   Phase 3  Phase 4  Phase 5  Phase 6
1        B       High 18102.778 29481.67 29481.67 11822.222 30737.78 21634.67 12768.00
2        B        Low  3860.694 15978.47 11175.69  7448.000 12768.00 11467.56 11467.56
3        B     Medium  5323.694 24974.44 15184.17  9221.333 15368.89 12768.00 12768.00
4        I       High 18102.778 74184.44 29481.67 44747.111 69160.00 45249.56 32245.11
5        I        Low  3860.694 26008.89 11175.69 16551.111 35910.00 16876.22 14275.33
6        I     Medium  5323.694 41156.11 15184.17 22373.556 44776.67 23378.44 16876.22
7       RV       High 18102.778 28373.33 29481.67 44747.111 69160.00 45249.56 32245.11
8       RV        Low  3860.694 14870.14 11175.69 16551.111 44776.67 16876.22 14275.33
9       RV     Medium  5323.694 22757.78 15184.17 22373.556 44776.67 23378.44 16876.22

我正在尝试找到一个简单的解决方案来在 cost.approach.difficulty 表中查找“方法”和“难度”的相应值。

例如,在 cost.table 中,我希望将第一行 Phase.0.Difficulty 列替换为 3860.694(因为它是“B”方法且难度较低。

有没有人有一个优雅、简单的解决方案来查找基于两列(或更多列)的值并沿多列替换值?

谢谢,

安德鲁

更新 -

有两个与使用合并相关的建议答案。我的目标是找到一个更简洁、简洁、优雅的解决方案。这是迄今为止我想出的最好的:

cost.approach.difficulty$Phase.0[match(paste(cost.table$Approach, cost.table$Phase.0.Difficulty), paste(cost.approach.difficulty$Approach, cost.approach.difficulty$Difficulty))]

这个解决方案的问题是我需要提前知道列名,而且看起来仍然像一个 hack。谁有更简洁的解决方案?

【问题讨论】:

  • 在cost.approach.difficulty 中使用空格的列名总是一个坏主意。可以用colnames(cost.approach.difficulty) <- make.names( colnames(cost.approach.difficulty) ) 修复它们吗?
  • Beasterfield,我并不担心名称(这只是一个例子)——真正的问题是你如何逐行遍历多个列,并替换基于值的从另一个数据框中查找两列。
  • 你真的应该担心这些名字。如果不出意外,具有格式奇怪的示例数据(名称或其他)会使这里的人使用该示例数据来帮助回答您的问题变得更加困难。

标签: r


【解决方案1】:

如果您希望这适用于可变数量的列,我建议将您的成本表和查找表重新调整为更标准化的格式。

首先,如果您以可重现的格式提供数据,回答这个问题会更容易:

# Create the example data
cost.table <- data.frame(
  "Identifier" = c("FS1", "FS2",  "FS3",  "FS4",  "FS5"),
  "Phase.0.Difficulty" = c("Low", "High", "High", "High", "High"),
  "Phase.1.Complexity" = c("Low", "High", "Low", "Medium", "Medium"),
  "Phase.2.Complexity" = c("Low", "High", "Low", "Medium", "Medium"),
  "Phase.3.Complexity" = c("Medium", "Medium", "High", "Medium", "High"),
  "Phase.4.Complexity" = c("Medium", "Medium", "High", "Medium", "Medium"),
  "Phase.5.Complexity" = c("High", "Medium", "High", "Medium", "Medium"),
  "Phase.6.Complexity" = c("High", "Medium", "High", "Medium", "Medium"),
  "Transaction.Feasibility" = c("Medium", "Medium", "Medium", "Medium", "Medium"),
  "Approach" = c("B", "I", "B", "I", "B"),
  stringsAsFactors = FALSE)

cost.approach.difficulty <- data.frame(
  "Approach" = c("B", "B", "B", "I", "I", "I", "RV", "RV", "RV"),
  "Difficulty" = c("High", "Low", "Medium", "High", "Low", "Medium", "High", "Low", "Medium"),
  "Phase.0" = c(18102.778, 3860.694, 5323.694, 18102.778, 3860.694, 5323.694, 18102.778, 3860.694, 5323.694),
  "Phase.1" = c(29481.67,15978.47, 24974.44, 74184.44, 26008.89, 41156.11, 28373.33, 14870.14, 22757.78),
  "Phase.2" = c(29481.67, 11175.69, 15184.17, 29481.67, 11175.69, 15184.17, 29481.67, 11175.69, 15184.17),
  "Phase.3" = c(11822.222, 7448, 9221.333, 44747.111, 16551.111, 22373.556, 44747.111, 16551.111, 22373.556),
  "Phase.4" = c(30737.78, 12768, 15368.89, 69160, 35910, 44776.67, 69160, 44776.67, 44776.67),
  "Phase.5" = c(21634.67, 11467.56, 12768, 45249.56, 16876.22, 23378.44, 45249.56, 16876.22, 23378.44),
  "Phase.6" = c(12768, 11467.56, 12768, 32245.11, 14275.33, 16876.22, 32245.11, 14275.33, 16876.22),
  stringsAsFactors = FALSE)

重新创建示例数据后,我使用了 reshape2 包中的 melt.data.frame 函数:

# Reshape the data
require(reshape2)

cost.table <- melt(cost.table, id.vars = c("Identifier", "Approach"), 
  value.name = "Size")
cost.table$Phase <- gsub("(\\w+\\.\\d+)\\.(\\w+)", "\\1", 
  as.character(cost.table$variable), perl = TRUE)
cost.table$Type <- gsub("(\\w+\\.\\d+)\\.(\\w+)", "\\2", 
  as.character(cost.table$variable), perl = TRUE)

head(cost.table)

  Identifier Approach           variable Size   Phase       Type
1        FS1        B Phase.0.Difficulty  Low Phase.0 Difficulty
2        FS2        I Phase.0.Difficulty High Phase.0 Difficulty
3        FS3        B Phase.0.Difficulty High Phase.0 Difficulty
4        FS4        I Phase.0.Difficulty High Phase.0 Difficulty
5        FS5        B Phase.0.Difficulty High Phase.0 Difficulty
6        FS1        B Phase.1.Complexity  Low Phase.1 Complexity

cost.approach.difficulty <- melt(cost.approach.difficulty, 
  id.vars = c("Difficulty", "Approach"), variable.name = "Phase")
cost.approach.difficulty$Phase <- as.character(cost.approach.difficulty$Phase)
cost.approach.difficulty$Type <- "Difficulty"
colnames(cost.approach.difficulty)[
  colnames(cost.approach.difficulty) == "Difficulty"] <- "Size"

head(cost.approach.difficulty)

    Size Approach   Phase     value       Type
1   High        B Phase.0 18102.778 Difficulty
2    Low        B Phase.0  3860.694 Difficulty
3 Medium        B Phase.0  5323.694 Difficulty
4   High        I Phase.0 18102.778 Difficulty
5    Low        I Phase.0  3860.694 Difficulty
6 Medium        I Phase.0  5323.694 Difficulty

一旦这两个表格采用标准化格式,您就可以调用merge:

cost.table.filled <- merge(cost.table, cost.approach.difficulty, 
  by = c("Approach", "Size", "Phase", "Type"), all.x = TRUE, all.y = FALSE)

然后,如果您没有查找某些列的值,您可以重新插入原始值(否则您最终会得到一堆 NA):

cost.table.filled$value[is.na(cost.table.filled$value)] <- 
  cost.table.filled$Size[is.na(cost.table.filled$value)]

然后你可以dcast把东西恢复成原来的格式:

cost.table.final <- dcast(cost.table.filled, Identifier + Approach ~ Phase + Type)

head(cost.table.final)

  Identifier Approach Phase.0_Difficulty Phase.1_Complexity Phase.2_Complexity Phase.3_Complexity Phase.4_Complexity Phase.5_Complexity Phase.6_Complexity Transaction.Feasibility_Transaction.Feasibility
1        FS1        B           3860.694                Low                Low             Medium             Medium               High               High                                          Medium
2        FS2        I          18102.778               High               High             Medium             Medium             Medium             Medium                                          Medium
3        FS3        B          18102.778                Low                Low               High               High               High               High                                          Medium
4        FS4        I          18102.778             Medium             Medium             Medium             Medium             Medium             Medium                                          Medium
5        FS5        B          18102.778             Medium             Medium               High             Medium             Medium             Medium                                          Medium

要替换所有列,我将 melt 每个查找表,然后将 cbind 将它们全部合并到一个查找表中。这样一来,您只需拨打merge 一次,无需担心更换 NA。

【讨论】:

  • Schaun,这是一个解决方案,但非常混乱且不太简洁。我发现简单地使用粘贴和匹配会更干净,但我很惊讶没有原生支持使用查找表。使用 reshape (在我看来这是不必要的)和合并数据框是不必要的复杂。我将发布一个使用 match 的简单示例,但我真的希望有人知道更好(阅读、简洁和优雅)的方法。
  • 这是迄今为止我发现的最干净的解决方案。 cost.approach.difficulty$Phase.0[匹配(粘贴(cost.table$Approach,cost.table$Phase.0.Difficulty),粘贴(cost.approach.difficulty$Approach,cost.approach.difficulty$Difficulty)) ]
  • 不雅的数据组织通常需要不雅的数据操作。您组织数据的方式很难创建简洁且可扩展的方法:您的查找表将有关类型、大小和阶段的信息保存在不同的位置,但您的 cost.table 将类型和阶段组合在一起。 “一栏一指标”组织减少了操作数据所需的代码总量。使用您拥有的结构,您的简洁解决方案需要您事先知道所有列名,这意味着即使这样也不是一个真正干净的解决方案。
  • SchaunW,感谢您对演示数据的建设性批评!与此同时,最优雅的解决方案似乎是粘贴和匹配。
  • 我并没有过多批评您的演示数据,而是试图解释为什么在这种情况下可能无法获得比您已经找到的更优雅的解决方案。
【解决方案2】:

在这种情况下,merge 应该可以解决问题:

cost.table <- merge(
  x = cost.table,
  y = cost.approach.difficulty[c("Approach", "Difficulty", "Phase.0")],
  by.x = c("Phase.0.Difficulty", "Approach"),
  by.y = c("Difficulty", "Approach"), sort = FALSE
)
cost.table$Phase.0.Difficulty <- NULL
names(cost.table)[names(cost.table) == "Phase.0"] <- "Phase.0.Difficulty"

cost.table
  Approach Identifier Phase.1.Complexity Phase.2.Complexity Phase.3.Complexity Phase.4.Complexity Phase.5.Complexity Phase.6.Complexity Transaction.Feasibility Phase.0.Difficulty
1        B        FS1                Low                Low             Medium             Medium               High               High                  Medium           3860.694
2        I        FS2               High               High             Medium             Medium             Medium             Medium                  Medium          18102.778
3        I        FS4             Medium             Medium             Medium             Medium             Medium             Medium                  Medium          18102.778
4        B        FS3                Low                Low               High               High               High               High                  Medium          18102.778
5        B        FS5             Medium             Medium               High             Medium             Medium             Medium                  Medium          18102.778

【讨论】:

  • Beasterfield,这是一种方法,但如果有很多列,可能会很笨拙。在这种情况下,每个“phase.x..”列都需要替换为查找值,而不仅仅是一列。我希望有一种使用“匹配”的方法来找到每个 phase.x 的相应值(跨所有列)。
【解决方案3】:

最简单的答案似乎是:

  • 使用粘贴组合查找列
  • 使用 match 从查找表中查找行号

下面的代码用一行完成了多列查找。

    cost.approach.difficulty$Phase.0[match(paste(cost.table$Approach, 
cost.table$Phase.0.Difficulty), paste(cost.approach.difficulty$Approach, 
cost.approach.difficulty$Difficulty))]

要在多列中循环,for 循环可以正常工作。

不幸的是,我希望有一个本机解决方案,它可能采用列向量并将它们组合起来进行查找,但我还没有找到。我将检查其他软件包,看看是否存在这样的功能。

【讨论】:

    猜你喜欢
    • 2016-06-08
    • 2017-11-28
    • 2021-12-29
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 2014-04-23
    • 1970-01-01
    相关资源
    最近更新 更多