【问题标题】:Faster way to run a for loop in R for this?为此在 R 中运行 for 循环的更快方法?
【发布时间】:2016-06-25 08:36:32
【问题描述】:

所以,这就是我的数据框的样子:

Product_Code      Publisher    Published_Date
AB1F                  A            2011
AB1F (A Version)      A            1999
TG1F (B Version)      B            2001
AB1Z (A Version)      A            2003
TG1F                  B            2006
GX1T                  C            2011

大约有 130 万行。

我要做的是针对具有相同发布者的行,我会在 Product_Code 中使用 grep() 来查找具有相同产品代码的行,而不管它们是什么版本。并将它们设置为具有最早的 Published_Date。

所以结果会是这样的:

Product_Code      Publisher    Published_Date
AB1F                  A            1999
AB1F (A Version)      A            1999
TG1F (B Version)      B            2001
AB1Z (A Version)      A            2003
TG1F                  B            2001
GX1T                  C            2011

我试过了

for (n in 1:nrow(df)) {
   A=which(grepl(df[n,1],df[,1])==TRUE & df[n,2]==df[,2])
   min.date=min(df[A,3])
   df[A,3]=min.date
}

我不确定这个 for 循环代码是否有效,因为我的计算机永远不会完成代码的运行。

任何帮助将不胜感激!

【问题讨论】:

  • 产品变体是否总是由“(”区分?如果是这样,我真的会开始 split Product_Code by ( 必须分隔字段。然后这个问题可以很容易地回答没有任何完全循环。
  • 没有。不幸的是,它们并不总是由特定的字符串(如“(”)来区分。例如,一些 Product_Code 将像 AB1F(版本 A)、AB1F - 版本 A、AB1F 版本 A、AB1F ver.A、AB1F 新等。
  • @Dominique 如果您的原始数据集没有显示在这里,我们怎么知道它的模式?
  • @akrun,很抱歉。这是我的错。我刚刚打印了 head()。但可以肯定的一件事是没有公认的模式。这就是为什么我的想法是在 for 循环中对每个 n 使用 grep() 来查找共享基本产品代码的行。
  • 注意,如果这是产品代码,可以用空格分隔,只取第一个字符串(代码)

标签: r


【解决方案1】:

我们可以使用data.table。将“data.frame”转换为“data.table”(setDT(df1))。我们删除与空格匹配的子字符串,后跟(,然后是使用sub 的多个字符之一,将其用作分组变量if 在'Product_Code'中有任何( 字符,然后我们@ 987654328@ 'A', 'B' 与来自 'Product_Code' 的子字符串,删除 NA,使用它来子集 'Published_Date',获取 minelse 返回 'Published_Date' 并分配(@ 987654331@) 到 'Published_Date'。

library(data.table)
setDT(df1)[, Published_Date := if(any(grep("\\(", Product_Code))) 
  min(Published_Date[na.omit(match(c("A", "B"), sub(".*\\((.).*", "\\1", Product_Code)))])
   else Published_Date , by = .(grp=sub("\\s+.*", "", Product_Code))]
     Product_Code Publisher Published_Date
#1:             AB1F         A           1999
#2: AB1F (A Version)         A           1999
#3: TG1F (B Version)         B           2001
#4: AB1Z (A Version)         A           2003
#5:             TG1F         B           2001
#6:             GX1T         C           2011

或者dplyr,我们separate'Product_Code'分为两列(“Product”,“Version”),按“Product”分组,我们mutate'Published_Date'基于if/else条件。

library(dplyr)
library(tidyr)
df1 %>% 
    separate(Product_Code, into = c("Product", "Version"), remove=FALSE) %>%
    group_by(Product) %>% 
    mutate(Published_Date = if(all(is.na(Version))) Published_Date
          else min(Published_Date[Version == Publisher & !is.na(Version)])) %>%
    ungroup() %>%   
    select(-Product, - Version)
#      Product_Code Publisher Published_Date
#             <chr>     <chr>          <int>
#1             AB1F         A           1999
#2 AB1F (A Version)         A           1999
#3 TG1F (B Version)         B           2001
#4 AB1Z (A Version)         A           2003
#5             TG1F         B           2001
#6             GX1T         C           2011

除了separate,我们也可以使用extract来避免警告信息

df1 %>% 
   extract(Product_Code, into = c("Product", "Version"), 
                     "(\\S+)\\s*\\(*(\\S*).*", remove = FALSE)%>%
   group_by(Product) %>%
   mutate(Published_Date = if(all(!nzchar(Version))) Published_Date
      else min(Published_Date[Version == Publisher])) %>%
   ungroup() %>%
   select(-Product, -Version)
#     Product_Code Publisher Published_Date
#             <chr>     <chr>          <int>
#1             AB1F         A           1999
#2 AB1F (A Version)         A           1999
#3 TG1F (B Version)         B           2001
#4 AB1Z (A Version)         A           2003
#5             TG1F         B           2001
#6             GX1T         C           2011

更新

如果没有特定的模式,我们可以为没有(且超过1个单词的元素创建一个(

df1$Product_Code <- sub("\\s+\\(*", " (", df1$Product_Code)

并使用上面的代码。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2013-01-12
  • 2021-07-07
  • 1970-01-01
  • 2020-11-21
  • 2019-07-03
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多