【问题标题】:Condtionally create new columns based on specific numeric values (keys) from existing column根据现有列中的特定数值(键)有条件地创建新列
【发布时间】:2015-10-18 10:46:45
【问题描述】:

我有一个 data.frame df,其中 x 列填充了整数 (1-9)。我想根据 x 的值更新列 y 和 z,如下所示:

if x is 1,2, or 3 | y = 1 ## if x is 1,4, or 7 | z = 1 
if x is 4,5, or 6 | y = 2 ## if x is 2,5, or 8 | z = 2 
if x is 7,8, or 9 | y = 3 ## if x is 3,6, or 9 | z = 3

下面是带有yz 所需输出的data.frame

df <- structure(list(x = c(1L, 2L, 3L, 3L, 4L, 2L, 1L, 2L, 5L, 2L, 
1L, 6L, 3L, 7L, 3L, 2L, 1L, 4L, 3L, 2L), y = c(1L, 1L, 1L, 1L, 
2L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 3L, 1L, 1L, 1L, 2L, 1L, 1L
), z = c(1L, 2L, 3L, 3L, 1L, 2L, 1L, 2L, 2L, 2L, 1L, 3L, 3L, 
1L, 3L, 2L, 1L, 1L, 3L, 2L)), .Names = c("x", "y", "z"), class = "data.frame", row.names = c(NA, 
-20L))

我可以编写一个带有多个 if 语句的 for 循环来逐行填充 yz。这似乎不是很 r:它不是矢量化的。有没有一种方法可以指定哪些数值对应于新的数值?像地图或键一样,指示哪些值将基于以前的值。

【问题讨论】:

  • 您想要的输出与df$y[12](规则为 2,值为 3)和 df$z[16](规则为 2,值为 3)的规则不匹配。
  • 是的,这是一个错误。

标签: r dictionary dataframe


【解决方案1】:

解决方案 #1:查找向量

假设我在评论中指出的不匹配是数据中的错误,而不是规则中的错误,那么您可以按如下方式完成:

x2y <- rep(1:3,each=3);
x2z <- rep(1:3,3);
df$y <- x2y[df$x];
df$z <- x2z[df$x];
df1 <- df; ## for identical() calls later
df;
##    x y z
## 1  1 1 1
## 2  2 1 2
## 3  3 1 3
## 4  3 1 3
## 5  4 2 1
## 6  2 1 2
## 7  1 1 1
## 8  2 1 2
## 9  5 2 2
## 10 2 1 2
## 11 1 1 1
## 12 6 2 3
## 13 3 1 3
## 14 7 3 1
## 15 3 1 3
## 16 2 1 2
## 17 1 1 1
## 18 4 2 1
## 19 3 1 3
## 20 2 1 2

上述解决方案取决于x 的域由从1 开始的连续整数值组成,因此直接索引到“查找向量”就足够了。如果x 从一个非常高的数字开始,但仍然是连续的,您可以通过在索引之前将x 的最小值减去一个来使此解决方案起作用。


解决方案 #2:查找表

如果您不喜欢这个假设,那么您可以使用查找表来完成任务:

library('data.table');
lookup <- data.table(x=1:9,y=x2y,z=x2z,key='x');
lookup;
##    x y z
## 1: 1 1 1
## 2: 2 1 2
## 3: 3 1 3
## 4: 4 2 1
## 5: 5 2 2
## 6: 6 2 3
## 7: 7 3 1
## 8: 8 3 2
## 9: 9 3 3
df[c('y','z')] <- lookup[df['x'],.(y,z)];
identical(df,df1);
## [1] TRUE

或基础 R 方法:

lookup <- data.frame(x=1:9,y=x2y,z=x2z);
lookup;
##   x y z
## 1 1 1 1
## 2 2 1 2
## 3 3 1 3
## 4 4 2 1
## 5 5 2 2
## 6 6 2 3
## 7 7 3 1
## 8 8 3 2
## 9 9 3 3
df[c('y','z')] <- lookup[match(df$x,lookup$x),c('y','z')];
identical(df,df1);
## [1] TRUE

解决方案 #3:算术表达式

另一种选择是设计与映射等效的算术表达式:

df$y <- (df$x-1L)%/%3L+1L;
df$z <- 3L--df$x%%3L;
identical(df,df1);
## [1] TRUE

此特定解决方案取决于您的映射恰好具有适合于算术描述的规律性这一事实。

在实现方面,它还利用了R precedence rules 的一些不明显的属性(实际上其他语言也是如此,例如C/C++Java),即一元负数高于模数,模数高于二进制减法,因此df$z的计算等价于3L-((-df$x)%%3L)

更详细地了解z 计算:不可能用df$x%%3 的直模来描述映射,因为3、6 和9 输入将修改为零。这可以通过一个简单的索引分配操作来解决,但我想实现一个更简单和纯粹的算术解决方案。要从 0 到 3,我们可以从 3 中减去 df$x%%3,但这会弄乱(反转)剩余的值。我意识到,通过取输入值的 negative 的 mod,我们将“预反转”它们,然后从 3 中减去所有它们将“正确”它们并且还将转换零3,根据需要。

【讨论】:

  • 还可以将x 设置为因子,将relevel 设置为创建yz 的因子。我发现这种方法在某些情况下更整洁,例如here
猜你喜欢
  • 2021-02-08
  • 1970-01-01
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多