【问题标题】:For loop for multiple indices多个索引的for循环
【发布时间】:2018-04-02 20:32:47
【问题描述】:

我知道在 R 中应该避免 for 循环,而应该使用矢量化操作。

我想用for 循环解决这个问题,然后尝试使用apply 系列,然后也在Rcpp 中。

我加载了一个包含一列密码(字母数字)的数据集。

一旦加载(示例,为了速度),我想根据某些条件“contains_lower_chars”、“contains_numbers”等创建具有值 (0,1) 的新列。

这是我尝试做的,但它不起作用 - 这意味着我创建的每一列都具有相同的值。

library(tidyverse)
set.seed(123)
# load dataset from url, skip the first 16 rows
df <- read.csv('http://datashaping.com/passwords.txt', header = F, skip = 16) %>%
  sample_frac(.001) %>% 
  rename(password = V1)

patterns = c("[a-z]","[A-Z]","[0-9]+")

df$has_lower <- 0 
df$has_upper <- 0
df$has_numeric <- 0

for(i in 1:nrow(df)){
    for(j in patterns){
        n <- ifelse(grepl(j, df$password[i]),1,0)
        }
    df$has_lower[i] <- n
    df$has_upper[i] <- n 
    df$has_numeric[i] <- n
}

我想到的输出是:

password has_lower has_upper has_numeric
Bigmaccas   1         1       0
0127515559  0         0       1
dbqky73p    1         0       1

【问题讨论】:

  • “不起作用”到底是什么意思?你有错误吗?一些意想不到的输出?寻求帮助时,您应该包含一个简单的reproducible example,其中包含可用于测试和验证可能解决方案的示例输入和所需输出。
  • @MrFlick 您可以重现运行上述代码的整个示例。它包含指向用于填充 df 的 URL 文件的链接。我将添加输出错误(我创建的每一列都有相同的值)
  • 但是你没有给出想要的结果。如果不设置种子,使用sample_frac() 之类的东西是无法重现的(另外,它确实依赖于代码中未明确提及的dplyr)。我不知道为什么当您为每个列分配相同的 n 值时,您会假设列会有所不同。
  • @MrFlick 已编辑
  • 我将删除 [rcpp] 标签,因为这与 Rcpp 无关。

标签: r for-loop apply


【解决方案1】:

如果我们只是命名您的模式向量,我们可以简化事情。例如

patterns = c(has_lower="[a-z]",
             has_upper="[A-Z]",
             has_numeric="[0-9]+")

for(pattern in names(patterns)) {
  df[, pattern] = as.numeric(grepl(patterns[pattern], df$password))
}

基本上我们只是遍历每个名​​称,获取与该名称对应的正则表达式,然后进行匹配并添加列。

【讨论】:

  • 它就像一个魅力。我唯一的问题是:df[, i] 是什么意思:添加第 i 列?
  • 它提取或添加一列。 i 可以是返回ith 列的数字,也可以是返回具有该名称的列的字符/字符串。
【解决方案2】:

数据框首先是一个列表。

所以,你可以这样做:

df[c("has_lower", "has_upper", "has_numeric")] <- 
  lapply(patterns, function(pattern) grepl(pattern, df$password) + 0)

使用+ 0L 而不是+ 0 是你想要整数而不是双精度数(我建议什么都不做并保持逻辑)。

【讨论】:

  • 我收到错误Error in :=(c("has_lower", "has_upper", "has_numeric"), lapply(patterns, : could not find function ":="。我做错了什么?我已经加载了data.table
【解决方案3】:

首先,您需要在 j 循环中更新 has.lower has.upper 和 has.numeric,否则您的 n 在这 3 种情况下保持不变。为此,您需要能够遍历列的名称 has.lower has.upper 和 has.numeric:

names <- c("has_lower","has_upper","has_numeric")

for(i in 1:nrow(df)){
  for(j in 1:length(patterns)){
    df[i,(names[j])] <- as.numeric(grepl(j, df$password[i]))
  }
}

使用applygrepl 已经矢量化这一事实的更快、更好、更紧凑的替代方案:

df[, c("has_lower","has_upper","has_numeric"):=lapply(patterns, function(x) grepl(x,df$password))]

注意(与您的问题无关):

我建议您使用 fread 函数来读取您的数据集,因为它非常大。

df = fread('http://datashaping.com/passwords.txt', header = F, skip = 16)%>%
  sample_frac(.001) %>% 
  rename(password = V1)

【讨论】:

  • 你能解释一下(用简单的话)df[i,(names[j])] 是什么意思吗?我很难理解这个......关于fread,如果你想了解它,我发布了一个问题here
  • df[i,(names[j])] 为以names[j] 命名的列选择行inames[j] 周围的括号告诉 R 它必须使用变量 names[j] 的值来查找 df 中的相应列。 df 中没有名为 names[j] 的列,但有名为 has_lowerhas_upper 等的列...
  • 谢谢!非常清楚。在第二种方法中,我收到错误Check that is.data.table(DT) == TRUE. Otherwise, := and :=(...) are defined for use in j, once only and in particular ways. See help(":=").
  • 那是我的错,我认为“df”是一个 data.table,因为我使用“fread”来读取你的数据。我会更新的。
  • 我添加了 setDT 并且它有效,即使它给了我“真/假”而不是 0/1
猜你喜欢
  • 2012-09-27
  • 2021-07-08
  • 2020-09-09
  • 1970-01-01
  • 2022-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多