【问题标题】:For loop to mutate multiple columnsFor循环改变多列
【发布时间】:2021-06-29 06:47:43
【问题描述】:

我有一个 tibble songs,它太大了,无法在这里分享。另外,没关系;该问题适用于任何只有 dbl 值的小标题。

我的想法是我之前选择了一行。它可以是其中任何一个,而无需任何先前的知识。我做的第一件事就是过滤掉它:

songs2 <- songs %>%
  anti_join(choice)

这行得通。

顺便说一句,choice 有一行。

现在,我创建了第二个 tibble(第三个,但在本文中是第二个),名为 dist,它只有 dbl 值(因此与 choice 共享列)。我想从dist 中的每一行中减去choice 中的值。

我试着写这个:

for (i in seq_along(distUseful)) {
  dist <- dist %>%
    mutate_(distUseful[i] = (.data[[i]] - choice[[i]]))
}

但它不起作用:

> for (i in seq_along(distUseful)) {
+   dist <- dist %>%
+     mutate_(distUseful[i] = (.data[[i]] - choice[[i]]))
Error: unexpected '=' in:
"  dist <- dist %>%
    mutate_(distUseful[i] ="
> }
Error: unexpected '}' in "}"

编辑:这是 songs2 中的前 10 行,按照 cmets 的要求。

structure(list(acousticness = c(0.991, 0.643, 0.993, 0.000173, 
0.295, 0.996, 0.992, 0.996, 0.996, 0.00682), artists = c("['Mamie Smith']", 
"[\"Screamin' Jay Hawkins\"]", "['Mamie Smith']", "['Oscar Velazquez']", 
"['Mixe']", "['Mamie Smith & Her Jazz Hounds']", "['Mamie Smith']", 
"['Mamie Smith & Her Jazz Hounds']", "['Francisco Canaro']", 
"['Meetya']"), danceability = c(0.598, 0.852, 0.647, 0.73, 0.704, 
0.424, 0.782, 0.474, 0.469, 0.571), duration_ms = c(168333, 150200, 
163827, 422087, 165224, 198627, 195200, 186173, 146840, 476304
), energy = c(0.224, 0.517, 0.186, 0.798, 0.707, 0.245, 0.0573, 
0.239, 0.238, 0.753), explicit = c(FALSE, FALSE, FALSE, FALSE, 
TRUE, FALSE, FALSE, FALSE, FALSE, FALSE), id = c("0cS0A1fUEUd1EW3FcF8AEI", 
"0hbkKFIJm7Z05H8Zl9w30f", "11m7laMUgmOKqI3oYzuhne", "19Lc5SfJJ5O1oaxY0fpwfh", 
"2hJjbsLCytGsnAHfdsLejp", "3HnrHGLE9u2MjHtdobfWl9", "5DlCyqLyX2AOVDTjjkDZ8x", 
"02FzJbHtqElixxCmrpSCUa", "02i59gYdjlhBmbbWhf8YuK", "06NUxS2XL3efRh0bloxkHm"
), instrumentalness = c(0.000522, 0.0264, 1.76e-05, 0.801, 0.000246, 
0.799, 1.61e-06, 0.186, 0.96, 0.873), key = c(5, 5, 0, 2, 10, 
5, 5, 9, 8, 8), liveness = c(0.379, 0.0809, 0.519, 0.128, 0.402, 
0.235, 0.176, 0.195, 0.149, 0.092), loudness = c(-12.628, -7.261, 
-12.098, -7.311, -6.036, -11.47, -12.453, -9.712, -18.717, -6.943
), mode = c(0, 0, 1, 1, 0, 1, 1, 1, 1, 1), name = c("Keep A Song In Your Soul", 
"I Put A Spell On You", "Golfing Papa", "True House Music - Xavier Santos & Carlos Gomix Remix", 
"Xuniverxe", "Crazy Blues - 78rpm Version", "Don't You Advertise Your Man", 
"Arkansas Blues", "La Chacarera - Remasterizado", "Broken Puppet - Original Mix"
), popularity = c(12, 7, 4, 17, 2, 9, 5, 0, 0, 0), release_date = c("1920", 
"1920-01-05", "1920", "1920-01-01", "1920-10-01", "1920", "1920", 
"1920", "1920-07-08", "1920-01-01"), speechiness = c(0.0936, 
0.0534, 0.174, 0.0425, 0.0768, 0.0397, 0.0592, 0.0289, 0.0741, 
0.0446), tempo = c(149.976, 86.889, 97.6, 127.997, 122.076, 103.87, 
85.652, 78.784, 130.06, 126.993), valence = c(0.634, 0.95, 0.689, 
0.0422, 0.299, 0.477, 0.487, 0.366, 0.621, 0.119), year = c(1920, 
1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920)), row.names = c(NA, 
-10L), class = c("tbl_df", "tbl", "data.frame"))

这是choice

structure(list(acousticness = 0.511, danceability = 0.403, duration_ms = 117395, 
    instrumentalness = 0.896, liveness = 0.108, loudness = -8.126, 
    popularity = 65, speechiness = 0.0514, tempo = 135.047, valence = 0.192), row.names = c(NA, 
-1L), class = c("tbl_df", "tbl", "data.frame"))

最后:

distUseful <- c("acousticness", "danceability", "duration_ms", "duration_ms", "instrumentalness", "liveness", "loudness", "popularity", "speechiness", "tempo", "valence")

编辑 2:只是事后的想法:如果您采用我之前引用的循环并查看它在单次迭代中的工作方式(您选择变量),它就可以工作。事实上,问题在于第一部分,distUseful[i] =,根据错误消息和代码。

编辑 3:例如,如果仅对第一列执行此操作,则会发生以下情况(因此第一列是正确的,其余的没有改变):

> dist %>%
+     mutate(acousticness = (dist[[1]] - choice[[1]]))
# A tibble: 174,388 x 10
   acousticness danceability duration_ms instrumentalness liveness loudness popularity speechiness tempo valence
          <dbl>        <dbl>       <dbl>            <dbl>    <dbl>    <dbl>      <dbl>       <dbl> <dbl>   <dbl>
 1        0.48         0.598      168333       0.000522     0.379    -12.6          12      0.0936 150.   0.634 
 2        0.132        0.852      150200       0.0264       0.0809    -7.26          7      0.0534  86.9  0.95  
 3        0.482        0.647      163827       0.0000176    0.519    -12.1           4      0.174   97.6  0.689 
 4       -0.511        0.73       422087       0.801        0.128     -7.31         17      0.0425 128.   0.0422
 5       -0.216        0.704      165224       0.000246     0.402     -6.04          2      0.0768 122.   0.299 
 6        0.485        0.424      198627       0.799        0.235    -11.5           9      0.0397 104.   0.477 
 7        0.481        0.782      195200       0.00000161   0.176    -12.5           5      0.0592  85.7  0.487 
 8        0.485        0.474      186173       0.186        0.195     -9.71          0      0.0289  78.8  0.366 
 9        0.485        0.469      146840       0.96         0.149    -18.7           0      0.0741 130.   0.621 
10       -0.504        0.571      476304       0.873        0.092     -6.94          0      0.0446 127.   0.119 

【问题讨论】:

  • 您应该分享一些数据,以便我们重现您的问题。即使您认为我们应该抽象整个事情并从概念上解决您的问题,但用一些数据来帮助您会容易得多。它不必是您的整个数据框。像粘贴 dput(head(data_frame_name, 10)) 这样的东西会有所帮助。
  • 好的,我添加了songs2choicedistUseful
  • 您想从此处的song2 行中减去choice
  • 是的,没错。我想我会在那里添加一个“预期输出”。
  • 使用 dplyr 肯定会比使用这种 for 循环方法做得更好

标签: r dplyr


【解决方案1】:

假设dist 是一个小标题,choice 是一个值向量(其长度等于dist 中的列数),我会尝试这样的事情:

amend_row <- function(amend_vals, ...) {
   ... - amend_vals
}

purrr::pmap(dist, ~ amend_row(amend_vals = choice, .)) %>%
   do.call(what = rbind, args = .) %>%
   as_tibble() %>% 
   purrr::set_names(nm = colnames(dist))

【讨论】:

  • 其实choicetibble,不过你说得对,我没想到用purrr...
  • 问题:set_names 在这里做什么?
  • 具有讽刺意味的是,这似乎对第一列非常有效,但其余的却给了我疯狂的数字...也许是因为 choicetibble 而不是向量?
  • 即使选择是一个向量,它仍然不起作用。我通过将choice 变成带有as.numeric() 的向量来测试它...
【解决方案2】:

我遇到了一些困难,因为我认为 names(choice) 和 distUsefull 不完全匹配。

我在循环之前将名称(选择)重新命名为 distUsefull:

distUseful<-names(choice)
dist<-df[distUseful]

然后,使用for loop 解决方案

for (i in 1:nrow(dist)){
        for (j in seq_along(distUseful)){
                dist[i,j]<-dist[i,j]-choice[1,j]
        }
}

这减去了所要求的值。

dist
# A tibble: 10 x 10
   acousticness danceability duration_ms instrumentalness liveness loudness popularity speechiness  tempo valence
          <dbl>        <dbl>       <dbl>            <dbl>    <dbl>    <dbl>      <dbl>       <dbl>  <dbl>   <dbl>
 1        0.48        0.195        50938          -0.895    0.271    -4.50         -53     0.0422   14.9    0.442
 2        0.132       0.449        32805          -0.870   -0.0271    0.865        -58     0.002   -48.2    0.758
 3        0.482       0.244        46432          -0.896    0.411    -3.97         -61     0.123   -37.4    0.497
 4       -0.511       0.327       304692          -0.0950   0.02      0.815        -48    -0.00890  -7.05  -0.150
 5       -0.216       0.301        47829          -0.896    0.294     2.09         -63     0.0254  -13.0    0.107
 6        0.485       0.0210       81232          -0.0970   0.127    -3.34         -56    -0.0117  -31.2    0.285
 7        0.481       0.379        77805          -0.896    0.0680   -4.33         -60     0.0078  -49.4    0.295
 8        0.485       0.0710       68778          -0.71     0.087    -1.59         -65    -0.0225  -56.3    0.174
 9        0.485       0.0660       29445           0.0640   0.0410  -10.6          -65     0.0227   -4.99   0.429
10       -0.504       0.168       358909          -0.023   -0.016     1.18         -65    -0.0068   -8.05  -0.073
> 

For 循环可能很慢。在这种情况下,我们嵌套了 for 循环,这可能是大数据帧的问题。 dplyr*apply()data.table 解决方案可能会更快。

使用mapply() 的更快的单行解决方案(仅在列上循环,x 和 y 的矢量化减法):

data.frame(mapply(function(x,y)x-y, dist, choice))
   acousticness danceability duration_ms instrumentalness liveness loudness popularity speechiness   tempo valence
1      0.480000        0.195       50938       -0.8954780   0.2710   -4.502        -53      0.0422  14.929  0.4420
2      0.132000        0.449       32805       -0.8696000  -0.0271    0.865        -58      0.0020 -48.158  0.7580
3      0.482000        0.244       46432       -0.8959824   0.4110   -3.972        -61      0.1226 -37.447  0.4970
4     -0.510827        0.327      304692       -0.0950000   0.0200    0.815        -48     -0.0089  -7.050 -0.1498
5     -0.216000        0.301       47829       -0.8957540   0.2940    2.090        -63      0.0254 -12.971  0.1070
6      0.485000        0.021       81232       -0.0970000   0.1270   -3.344        -56     -0.0117 -31.177  0.2850
7      0.481000        0.379       77805       -0.8959984   0.0680   -4.327        -60      0.0078 -49.395  0.2950
8      0.485000        0.071       68778       -0.7100000   0.0870   -1.586        -65     -0.0225 -56.263  0.1740
9      0.485000        0.066       29445        0.0640000   0.0410  -10.591        -65      0.0227  -4.987  0.4290
10    -0.504180        0.168      358909       -0.0230000  -0.0160    1.183        -65     -0.0068  -8.054 -0.0730

【讨论】:

  • 嘿,你是对的!我在distUseful 中放了两份"duration_ms"... Ops
  • 问题是seq_along() 计算向量中元素的数量。 nrow() 只是一个数字。只需将其更改为 1:nrow(dist)choice[1,j]
  • 好吧,这行得通,但它也非常慢......也许有更快的方法? (请记住,我正在使用 174.388 长的 tibble 进行此操作,可用 here
  • 我发现了一个非常简单的带有 mapply 的单线,@ÉricoPatto。编辑了我的答案。这必须比嵌套的 for 循环快。
  • 万岁!这是迄今为止最快的!整个小标题都用了0.027s elapsed! (我刚刚将data.frame 更改为as_tibble)万岁!
【解决方案3】:

玩弄大家的建议,我想出了很多想法。只有其中一个有效。

我使用了@Johny 函数的修改版本(并纠正了@GuedesBF 提到的我的向量distUseful),使用了不要循环的建议并提出了apply

amend_row <- function(data) {
  data - as.numeric(choice)
}

dist %>%
  apply(X = ., FUN = amend_row, MARGIN = 1) %>%
  t() %>%
  as_tibble()

这给了我:

> dist %>%
+   apply(X = ., FUN = amend_row, MARGIN = 1) %>%
+   t() %>%
+   as_tibble()
# A tibble: 174,388 x 10
   acousticness danceability duration_ms instrumentalness liveness loudness popularity speechiness  tempo valence
          <dbl>        <dbl>       <dbl>            <dbl>    <dbl>    <dbl>      <dbl>       <dbl>  <dbl>   <dbl>
 1        0.48         0.195       50938          -0.895    0.271    -4.50         -53     0.0422   14.9    0.442
 2        0.132        0.449       32805          -0.870   -0.0271    0.865        -58     0.002   -48.2    0.758
 3        0.482        0.244       46432          -0.896    0.411    -3.97         -61     0.123   -37.4    0.497
 4       -0.511        0.327      304692          -0.0950   0.0200    0.815        -48    -0.00890  -7.05  -0.150
 5       -0.216        0.301       47829          -0.896    0.294     2.09         -63     0.0254  -13.0    0.107
 6        0.485        0.021       81232          -0.0970   0.127    -3.34         -56    -0.0117  -31.2    0.285
 7        0.481        0.379       77805          -0.896    0.068    -4.33         -60     0.0078  -49.4    0.295
 8        0.485        0.071       68778          -0.710    0.087    -1.59         -65    -0.0225  -56.3    0.174
 9        0.485        0.066       29445           0.064    0.0410  -10.6          -65     0.0227   -4.99   0.429
10       -0.504        0.168      358909          -0.0230  -0.016     1.18         -65    -0.0068   -8.05  -0.073
# … with 174,378 more rows

在极短的时间内。

编辑:这是仅使用前 1000 行的时差:

# MY SOLUTION
> dist <- songs2 %>%
+   select(all_of(distUseful)) %>%
+   head(1000)
> system.time(dist %>%
+               apply(X = ., FUN = subtraction, MARGIN = 1) %>%
+               t() %>%
+               as_tibble())
   user  system elapsed 
  0.006   0.000   0.006 
# THE FUNCTION SOLUTION – DIDN'T WORK PROPERLY (last I checked)
> amend_row <- function(amend_vals, ...) {
+   ... - amend_vals
+ }
> system.time(purrr::pmap(dist, ~ amend_row(amend_vals = choice, .)) %>%
+               do.call(what = rbind, args = .) %>%
+               as_tibble() %>% 
+               purrr::set_names(nm = colnames(dist)))
   user  system elapsed 
  1.222   0.016   1.261 
# NOT A LOT OF TIDYVERSE SOLUTION – SLOOOOOWWWWWW
> system.time(for (i in 1:nrow(dist)){
+   for (j in seq_along(distUseful)){
+     dist[i,j]<-dist[i,j]-choice[1,j]
+   }
+ })
   user  system elapsed 
  7.359   0.046   7.482 

【讨论】:

  • 优秀。如果你能用system.time()显示你得到的时间差异,那就太好了
  • 另外两个解决方案甚至都没有完成。我知道它更快,因为当我只使用head() 运行它时它会更快,而且因为......好吧,它实际上完成了。但我会把区别放在head() 上,好主意。
猜你喜欢
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 2013-03-21
  • 1970-01-01
  • 2022-12-03
  • 2021-07-20
相关资源
最近更新 更多