【问题标题】:Performing a for loop on a matrix instead of a data frame在矩阵而不是数据框上执行 for 循环
【发布时间】:2017-09-12 02:53:07
【问题描述】:

我正在执行一个相当复杂的线性回归,其中涉及使用 for 循环有条件地在新列中创建虚拟变量。到目前为止,我一直在几个数据帧中这样做,将它们转换为矩阵,然后将它们转换为稀疏矩阵,然后加入;但是,我已经达到了计算机的极限。抱歉,如果这让您感到困惑 - 我已尝试尽可能简化流程。

编辑 - 将所有数字示例添加到原始问题。

这里是所有数值的源数据:

df <- data.frame(matrix(nrow = 9, ncol = 4))
df$X1 <- c(5, 1, 2, 0, 4, 8, 7, 6, 0)
df$X2 <- c(10001, 10001, 10001, 10003, 10003, 10003, 10002, 10002, 10002) 
df$X3 <- c(10002, 10002, 10002, 10001, 10001, 10001, 10003, 10003, 10003) 
df$X4 <- c(10001, 10001, 10001, 10003, 10003, 10003, 10002, 10002, 10002)
names(df) <- c("response", "group_1", "group_2", "exclude")

看起来像什么:

  response group_1 group_2 exclude
1        5   10001   10002   10001
2        1   10001   10002   10001
3        2   10001   10002   10001
4        0   10003   10001   10003
5        4   10003   10001   10003
6        8   10003   10001   10003
7        7   10002   10003   10002
8        6   10002   10003   10002
9        0   10002   10003   10002

源数据(请参阅上面的编辑):

df <- data.frame(matrix(nrow = 9, ncol = 4))
df$X1 <- c(5, 1, 2, 0, 4, 8, 7, 6, 0)
df$X2 <- c("blue", "blue", "blue", "yellow", "yellow", "yellow", "green", "green", "green") 
df$X3 <- c("green", "green", "green", "blue", "blue", "blue", "yellow", "yellow", "yellow") 
df$X4 <- c("blue", "blue", "blue", "yellow", "yellow", "yellow", "green", "green", "green")
names(df) <- c("response", "group_1", "group_2", "exclude") 

这是数据的简化版本:

  response group_1 group_2 exclude
1        5    blue   green    blue
2        1    blue   green    blue
3        2    blue   green    blue
4        0  yellow    blue  yellow
5        4  yellow    blue  yellow
6        8  yellow    blue  yellow
7        7   green  yellow   green
8        6   green  yellow   green
9        0   green  yellow   green

从上面的数据中,我使用以下函数找到了“group_1”和“group_2”中的唯一变量:

fun_names <- function(x) {
  row1 <- unique(x$group_1)
  row2 <- unique(x$group_2)
  mat <- data.frame(matrix(nrow = length(row1) + length(row2), ncol = 1))
  mat[1] <- c(row1, row2)
  mat_unique <- data.frame(mat[!duplicated(mat[,1]), ])
  names(mat_unique) <- c("ID")

  return(mat_unique)
}
df_unique <- fun_names(df)

这将返回以下数据框:

      ID
1   blue
2 yellow
3  green

然后,对于每种颜色(“ID”),如果颜色在每一行中并且颜色与“排除”列值不匹配,我将创建一个值为 1 的新列。循环如下所示:

for(name in df_unique$ID) {
  df[paste(name)] <- 
    ifelse(df$group_1 == name & df$exclude != name | 
           df$group_2 == name & df$exclude != name, 1, 0)
}

运行此循环会返回最终的data.frame,如下所示:

编辑 这是最终df的数值数据:

  response group_1 group_2 exclude 10001 10003 10002
1        5   10001   10002   10001     0     0     1
2        1   10001   10002   10001     0     0     1
3        2   10001   10002   10001     0     0     1
4        0   10003   10001   10003     1     0     0
5        4   10003   10001   10003     1     0     0
6        8   10003   10001   10003     1     0     0
7        7   10002   10003   10002     0     1     0
8        6   10002   10003   10002     0     1     0
9        0   10002   10003   10002     0     1     0

这是原始数据:

  response group_1 group_2 exclude blue yellow green
1        5    blue   green    blue    0      0     1
2        1    blue   green    blue    0      0     1
3        2    blue   green    blue    0      0     1
4        0  yellow    blue  yellow    1      0     0
5        4  yellow    blue  yellow    1      0     0
6        8  yellow    blue  yellow    1      0     0
7        7   green  yellow   green    0      1     0
8        6   green  yellow   green    0      1     0
9        0   green  yellow   green    0      1     0

所以,我的问题是:如果原始数据是矩阵(而不是数据框),我该如何执行这个循环?由于循环正在修改数据框,因此我需要将该数据框转换为矩阵,以便将其转换为稀疏矩阵 - 这种 data.framedata.matrix 的转换对我的机器来说过于密集。

在上面的for 循环之前,我已将代码中的所有内容转换为矩阵表示法,但我无法弄清楚如何在修改 R 中的矩阵(而不是数据框)时以这种方式打印新列)。基本上,我希望有人可以帮助我修改for 循环,以便它可以在矩阵上工作。有人有什么建议吗?

编辑 我忘了提到源数据需要保留它的分组 - group_by(response, group_1, group_2, exclude)。此外,df 对象需要以矩阵形式开始,以消除data.framedata.matrix 的转换。

EDIT2 我没有提到这一点,但是在我运行整个过程之前,所有数据都被索引并转换为数值。所以示例中的df 对象实际上只是数字。

【问题讨论】:

  • 转换为矩阵会将所有数字对象呈现为字符类型。你想要那个吗?为什么需要群组信息?
  • 我有一个额外的步骤,在将所有数据设为矩阵之前将其逐项列出并将其转换为数字(它保存在另一个数据框中,因此我可以在执行回归后将其转换回来)。我将在编辑中指定。很抱歉造成混乱。
  • 我可能错了,因为我不太确定您的用例,但 factor 变量被单热编码以获取 numeric 功能,factor 变量已从数据中删除因为信息被新编码的变量捕获。
  • 如果您的数据全是数字(整数?),请提供全数字数据的示例。
  • 已添加。当我最初提交时,我应该意识到这一点。我应该只提交一个新的提交吗?这越来越长了。

标签: r matrix dataframe data.table sparse-matrix


【解决方案1】:

使用稀疏矩阵进行虚拟编码:

m <- as.matrix(df)

groups <- unique(as.vector(m[, grep("group", colnames(m))]))
tmp <- lapply(groups, function(x, m) 
  which((m[, "group_1"] == x | m[, "group_2"] == x) & m[, "exclude"] != x),
       m = m)

j = rep(seq_along(tmp), lengths(tmp))
i = unlist(tmp)

library(Matrix)
dummies <- sparseMatrix(i, j, dims = c(nrow(m), length(groups)))
colnames(dummies) <- groups

M <- Matrix(as.matrix(df))
cbind(M, dummies)
#9 x 7 Matrix of class "dgeMatrix"
#     response group_1 group_2 exclude 10001 10003 10002
#[1,]        5   10001   10002   10001     0     0     1
#[2,]        1   10001   10002   10001     0     0     1
#[3,]        2   10001   10002   10001     0     0     1
#[4,]        0   10003   10001   10003     1     0     0
#[5,]        4   10003   10001   10003     1     0     0
#[6,]        8   10003   10001   10003     1     0     0
#[7,]        7   10002   10003   10002     0     1     0
#[8,]        6   10002   10003   10002     0     1     0
#[9,]        0   10002   10003   10002     0     1     0

【讨论】:

  • 抱歉耽搁了。好吧,所以我认为这会奏效!我正在尝试修改它以立即在预期数据上运行,但在初始测试中,我认为我可以完成这项工作。您是否有理由在此处创建一个新对象进行绑定 - M &lt;- Matrix(as.matrix(df))?当我使用原始的 m 矩阵 - cbind(m, dummies) 绑定假人时 - 它提供了看起来像备用矩阵但实际上是 "dgCMatrix" 类的东西。我是否需要创建一个新对象才能使其工作?
  • 我已经能够在我的代码中完全实现这一点。非常感谢!
【解决方案2】:

这对您的矩阵来说是否过于激烈?它使用 dplyrtidyr 完全取消 for 循环:

library(dplyr)
library(tidyr)

m = df %>% 
    mutate(group = ifelse(group_1 == exclude, group_2, group_1), ones = 1) %>%
    select(response, group, ones) %>%
    spread(key = group, value = ones, fill = 0) %>%
    as.matrix

【讨论】:

  • 试了之后才发现原来我并没有说明需要保留原始数据的分组和顺序——group_by(response, group_1, and group_2, exclude)。我还需要保留原始数据......这就是我使用循环的原因。另外,我认为最后的 as.matrix 可能最终会占用大量内存?不过我可能会误解。
  • 您可以将as.matrix 替换为merge(df, by = "response") 并取回类似于您问题中的最终数据框的内容?我没有理解为什么需要转换为矩阵。我可能有点跑题了,但你的最终目标是什么?
  • 不用担心。好的,所以我的最终目标是两个稀疏矩阵,它们是 300 万行 x 1400 列,我 rbind 到一个大稀疏矩阵中。目前,我一直将每个都制作为 data.frame,然后转换为稀疏矩阵(清理中间的内存)。但是,我有更多需要包含的数据,但由于内存限制,我不能包含(最终目标是一个稀疏矩阵,即约 700 万行 x 约 4000 列)。所以,我希望我能完全按照我正在做的事情做,但是从 matrix 而不是 data.frame 开始,然后更新代码以使用 matrix
  • 我建议在您的问题中添加data.table 标签(您可能可以去掉loops 标签为其腾出空间)。这听起来像是涉及data.table 库的问题,这超出了我的专业范围。
【解决方案3】:

所以我从这样的矩阵开始:

m <- matrix(nrow = 9, ncol = 4)
m[,1]<- c(5, 1, 2, 0, 4, 8, 7, 6, 0)
m[,2] <- c("blue", "blue", "blue", "yellow", "yellow", "yellow", "green", "green", "green") 
m[,3] <- c("green", "green", "green", "blue", "blue", "blue", "yellow", "yellow", "yellow") 
m[,4] <- c("blue", "blue", "blue", "yellow", "yellow", "yellow", "green", "green", "green")
colnames(m) <- c("response", "group_1", "group_2", "exclude")

>m
 #    response group_1  group_2  exclude 
 #[1,] "5"      "blue"   "green"  "blue"  
 #[2,] "1"      "blue"   "green"  "blue"  
 #[3,] "2"      "blue"   "green"  "blue"  
 #[4,] "0"      "yellow" "blue"   "yellow"
 #[5,] "4"      "yellow" "blue"   "yellow"
 #[6,] "8"      "yellow" "blue"   "yellow"
 #[7,] "7"      "green"  "yellow" "green" 
 #[8,] "6"      "green"  "yellow" "green" 
 #[9,] "0"      "green"  "yellow" "green"

使用包dummies' dummy()函数:

one_hot_encoded_vars = dummy(x="group_2", data = m))
>one_hot_encoded_vars
 #        group_2blue group_2green group_2yellow
 #[1,]           0            1             0
 #[2,]           0            1             0
 #[3,]           0            1             0
 #[4,]           1            0             0
 #[5,]           1            0             0
 #[6,]           1            0             0
 #[7,]           0            0             1
 #[8,]           0            0             1
 #[9,]           0            0             1

创建一个包含所有变量的数字矩阵:

finalmatrix = cbind(as.numeric(m[,'response']),dummy(x = 'group_1',data = m),
    dummy(x = 'group_2',data = m),dummy(x = 'exclude',data=m))

>finalmatrix
#             group_1blue group_1green group_1yellow group_2blue group_2green group_2yellow excludeblue excludegreen
 #[1,] 5           1            0             0           0            1             0           1            0
 #[2,] 1           1            0             0           0            1             0           1            0
 #[3,] 2           1            0             0           0            1             0           1            0
 #[4,] 0           0            0             1           1            0             0           0            0
 #[5,] 4           0            0             1           1            0             0           0            0
 #[6,] 8           0            0             1           1            0             0           0            0
 #[7,] 7           0            1             0           0            0             1           0            1
 #[8,] 6           0            1             0           0            0             1           0            1
 #[9,] 0           0            1             0           0            0             1           0            1
 #         excludeyellow
 #[1,]             0
 #[2,]             0
 #[3,]             0
 #[4,]             1
 #[5,]             1
 #[6,]             1
 #[7,]             0
 #[8,]             0
 #[9,]             0

如果您想保留群组信息,您可以:

 finalmatrix = cbind(m, finalmatrix)

但是finalmatrix 将是字符类型对象。

【讨论】:

  • 这是否考虑了 group_1 和 group_2 中的所有变量?在简化时,我可能会产生误导......但是所有需要变成虚拟变量的变量都不包含在我的数据中的每个组中。对不起,如果这令人困惑。可能有变量在 group_1 中而不在 group_2 中(反之亦然),这就是为什么我一直在使用单独的过程来确定要添加哪些虚拟变量。
  • @Luke_ew 您可以分别转换group_1group_2exclude,然后将它们与response 合并。模型需要知道相应组中存在哪些值以及排除了哪些值。
  • @Luke_ew 如果您放弃分组变量,您将节省再次转换为数字的步骤。您的模型应类似于:response ~ group_1yellow + group_1blue + group_1green + group_2green + group_2yellow + group_2blue + excludeyellow + excludeblue + excludegreen.
  • 好的,有道理。我只是想弄清楚我将如何做到这一点。在实际数据中,有 12 个组......我有点过头了......你能举个例子来说明我将如何去做吗?也许我没有完全理解。
  • @Luke_ew 你必须阅读一种热编码,以掌握这个概念。
猜你喜欢
  • 2014-10-13
  • 2014-11-21
  • 2013-12-21
  • 2021-01-29
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
  • 1970-01-01
  • 2016-08-21
相关资源
最近更新 更多