简化双循环

时间:2017-10-30 10:09:33

标签: r for-loop matrix

我有两个矩阵。矩阵A有14个变量(但我只对本练习的五个变量感兴趣),矩阵B有五个变量。矩阵A有250,000个观测值,矩阵B有23,000个观测值。变量是非数字字符(文本)。

我的目标是用矩阵B中第4列和第5列的信息填充矩阵A的第9列和第10列,如果矩阵A中的第1,2和8列等于矩阵B中的第1,2和3列

我编写的代码是一个双循环,大约需要10天才能运行。我正在寻找一种方法来简化代码,使其更高效,运行速度更快(理想情况下在几个小时内)。

代码如下:

    for (i in 1:nrow(matrix_b)) {
      for (j in 1:nrow(matrix_a)) {
        if (matrix_a[j,1]==matrix_b[i,1]) {
          if (matrix_a[j,2]==matrix_b[i,2]) {
            if (matrix_a[j,8]==matrix_b[i,3]) {
              matrix_a[j,9]<-matrix_b[i,4]
              matrix_a[j,10]<-matrix_b[i,5]
            }
          }
        }
      }
    }

一个例子是:

    # Matrix A
          [,1]   [,2]         [,8]            [,9]       [,10]
    [1,]   Z10   11 Nov 2012   Rocko Large     NA         NA


    # Matrix B
          [,1]   [,2]         [,3]            [,4]       [,5]
    [1,]   Z10   11 Nov 2012   Rocko Large     Aldi       CFO

在这种情况下,代码应在单元格[1,9]中插入“Aldi”,在矩阵A的单元格[1,10]中插入“CFO”,因为单元格[1,1],[1,2],矩阵A中的[1,8]与矩阵B中的单元[1,1],[1,2]和[1,3]相同。

如果数据看起来像这样(第2列中的日期不同),代码不应该做任何事情;即,将单元格[1,9]和[1,10]保留在矩阵A中。

    # Matrix A
          [,1]   [,2]         [,8]            [,9]       [,10]
    [1,]   Z10   15 Dec 2013   Rocko Large     NA         NA


    # Matrix B
          [,1]   [,2]         [,3]            [,4]       [,5]
    [1,]   Z10   11 Nov 2012   Rocko Large     Aldi       CFO

2 个答案:

答案 0 :(得分:2)

这是一种完全避免循环的方法,而是使用连接来实现相同的效果:

set.seed(1)

# Create some fake data with the given dimensions
matrix_a <- matrix(sample(letters, 14 * 250000, replace = T), ncol = 14)
matrix_b <- matrix(sample(letters, 5 * 23000, replace = T), ncol = 5)

library(dplyr)

# Convert matrices to data frames
df_a <- as_tibble(matrix_a)
df_b <- as_tibble(matrix_b)

# Simplify df_b so that multiple rows from df_b don't match the same row
# in df_a: this is implied by the loop in the question, and isn't necessary
# to do for the real data, if the condition holds.
df_b <- df_b %>% 
  distinct(V1, V2, V3, .keep_all = T)

new <- df_a %>% 
  # join columns from b to a that match on the specified variables
  left_join(df_b %>% rename_all(~ paste0("b_", .)),
            by = c(V1 = "b_V1", V2 = "b_V2", V8 = "b_V3")) %>%
  # if there was a match in b, replace value in a with the value from b
  mutate(
    V9  = if_else(!is.na(b_V4), b_V4, V9),
    V10 = if_else(!is.na(b_V5), b_V5, V10)
  ) %>% 
  # drop the added columns from b
  select(-starts_with("b_"))

并且花费的时间:加入和变异在我的机器上花了0.17秒。

如果您想了解dplyr中提供的数据争论工具的更多信息,可以选择package vignettewebsite

检查应该更改的行,确实已更改:

should_change <- df_a %>% 
  mutate(row_id = row_number()) %>% 
  semi_join(df_b, by = c("V1", "V2", V8 = "V3"))

new %>%
  mutate(row_id = row_number()) %>% 
  select(row_id, V9, V10) %>% 
  inner_join(should_change %>% select(row_id, V9, V10),
             by = "row_id", suffix = c("_new", "_old"))
#> # A tibble: 181,708 x 5
#>    row_id V9_new V10_new V9_old V10_old
#>     <int>  <chr>   <chr>  <chr>   <chr>
#>  1      1      j       k      p       m
#>  2      3      n       e      f       h
#>  3      4      s       n      v       s
#>  4      6      m       r      j       n
#>  5      7      c       v      t       k
#>  6      8      m       n      u       y
#>  7      9      m       x      l       r
#>  8     10      e       a      z       v
#>  9     11      e       q      l       k
#> 10     12      o       f      z       q
#> # ... with 181,698 more rows

答案 1 :(得分:-1)

使用数据表需要几秒钟。 我可以在这里举个例子:

rm(list = ls())

A = replicate(14, round(rnorm(250000),digits = 2))
B = replicate(5,round(rnorm(250000),digits = 2))

只需创建两个矩阵

library(data.table)
DTA <- setDT(as.data.frame(A))
colnames(DTA) <- paste0("A",seq(dim(DTA)[2]))
DTB <- setDT(as.data.frame(B))
colnames(DTB) <- paste0("B",seq(dim(DTB)[2]))

DT <- cbind(DTA,DTB)

这里我只是通过将列连接在一起来创建一个唯一的数据表DT,将列名设置为A1到A15为A,B1到B5为B.它允许我们通过设置条件设置两个矩阵的条件在一个唯一的data.table。列上。

DT[A1 == B1 & A2 == B2 & A3 == B8, A9 := B4]
DT[A1 == B1 & A2 == B2 & A3 == B8, A10 := B5]

语法是DT [i,j,by]。列相等的条件位于括号的第一部分,第二部分则通过以下方式设置值:=

它与文本或因子的工作方式相同。

希望有所帮助