循环数据帧......以及为什么这是错误的方法

时间:2013-07-03 12:20:33

标签: r

假设我有几个数据帧,我想循环遍历每个数据帧的行并执行某些操作,例如使用httr包发送SMS消息。我可以创建几个循环,但我怀疑答案是“不要使用循环!”。

df1 <- data.frame(A=1:10, B=2:11, C=3:12) # imagine these columns hold words and phone numbers
df2 <- data.frame(A=4:13, B=5:14, C=6:15)

# loop for df1
for (n in 1:nrow(df1)) { # imagine that each row is a person
  # get details for each person (row)
  sms1 <- df1$A[n]
  sms2 <- df1$B[n]
  sms3 <- df1$C[n]
  # create personalized message to send
  sms <- paste(sms1, sms2, sms3, sep=" ")
  # here I would use the POST() function of httr to send a personalized SMS to each person, but that is not important
}

# loop for df2
for (n in 1:nrow(df1)) {
  sms1 <- df2$A[n]
  sms2 <- df2$B[n]
  sms3 <- df2$C[n]
  sms <- paste(sms1, sms2, sms3, sep=" ")
  # here I would use the POST() function of httr to send a personalized SMS, but that is not important
}

但我真正想做的是创建一个外部循环来遍历每个数据帧。类似的东西:

dfs <- c("df1", "df2")
# loop over dfs
for (d in dfs) {
  for (n in 1:nrow(d)) {
    sms1 <- d$A[n]
    sms2 <- d$B[n]
    sms3 <- d$C[n]
    sms <- paste(sms1, sms2, sms3, sep=" ")
    # here I would use the POST() function of httr to send a personalized SMS, but that is not important
  }
}

但我知道这不行。我d中的sms1 <- d$A[n]不会被视为sms1 <- df1$A[n]sms1 <- df2$A[n]

有没有办法做这个循环?更好的是,正确的应用方法是什么?

更新

以下是我需要对两个数据框中的每一行执行POST步骤的示例,以便向每个人(行)发送个性化消息:

# let's say that sms3 in my example is a phone number
# let's also say that I define the following objects once outside of the loop:
  # url, username, password, account, source, network

# when I paste together the following objects, I get string that is formatted for my API gateway. 
send <- paste0(url, username, password, account, source, sms3, 
                 sms, network)
POST(send)

这将在我原始帖子的评论中提到的循环:

# remove these paste steps from the loops as recommended in the answers
df1$sms <- paste(df2$A, df2$B)
df2$sms <- paste(df2$A, df2$B)

dfs <- c("df1", "df2")

# loop over dfs
for (d in dfs) {
  for (n in 1:nrow(d)) {
    sms3 <- d$C[n] # to get phone number
    send <- paste0(url, username, password, account, source, sms3, sms, network)
    POST(send)
  }
}

2 个答案:

答案 0 :(得分:3)

您不需要将每一行循环到paste个元素。相反,您只需为每个数据框调用一次paste

df1$sms <- paste(df1$A, df1$B, df1$C)

然后,您可以再次调用df1$send来创建paste,或者根据您的具体需求,只使用一个paste

现在您已在df1$send中拥有所需的一切,您只需在每个元素上调用POST即可。 POST没有矢量化,因此您必须以某种方式迭代元素。例如:

sapply(df1$send, POST)

您可以为df2再次执行此操作,但另一种方法是创建数据帧列表,然后循环,为每个数据帧执行相同的paste操作。例如:

my.dfs <- list(df1, df2)
for (df in my.dfs) {
  df$sms <- paste(df$A, df$B, df$C)
  ...
  sapply(df$send, POST)
}

(你可以也循环遍历字符串的名字,就像你原来的那样,然后获得与每个字符串相对应的实际对象:df <- get(d)。但我认为这里没有理由更喜欢这个。)

更好的方法是将df1df2合并到一个数据框中。您可以创建一个列来区分这两个组。然后,您只需要在一个数据框上pastePOST

comprehensive.df$sms <- paste(comprehensive.df$A, comprehensive.df$B, comprehensive.df$C)
...
sapply(comprehensive.df$send, POST)

如何执行此操作取决于数据框的不同程度。如果它们略有不同,您可以使用rbind.fill中的plyr来处理缺少的列。如果它们具有不同的列名等,您可以提取公共列并进行一些重命名。我只是认为,如果你在进行手术之前进行清洁和合并,你所做的事情会更清楚。

可以使用for循环。事实上,如果你查看apply的来源,你会发现它使用了for循环。实际的收益不是来自使用apply,而是来自通过将整个对象传递给函数来利用向量化,如上面使用paste所做的那样。这更具可读性(因为它是一条直接的单行)并且可能性能更好(因为你只需要调用函数一次)。

答案 1 :(得分:3)

只需重新绑定数据框架而不是循环:

df <- rbind(df1, df2)
df$sms <- paste(df$A, df$B, df$C)