• R语言已解决
  • 【已解决】对数据框中满足某些条件的数据的重写

比如对数据框mtcars,我想对满足某些条件比如行 2到5,列为cyl的数据进行重写,重写的条件为如果该值小于 6 就重写为 2:

library(dplyr)

m1 <- mtcars %>% 
  filter(row_number() %in% 2:5) %>% 
  mutate_at(vars(cyl), ~ ifelse(. < 6, . <- 2, . <- 6))

rbind(as.matrix(mtcars)[1, ], as.matrix(m1), as.matrix(mtcars)[6:length(mtcars$mpg), ])
#>                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#>                     21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#>                     21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#>                     22.8   2 108.0  93 3.85 2.320 18.61  1  1    4    1
#>                     21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
#>                     18.7   6 360.0 175 3.15 3.440 17.02  0  0    3    2
#> Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
#> Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
#> Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
#> Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
#> Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
#> Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
#> Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
#> Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
#> Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
#> Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
#> Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
#> Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
#> Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
#> Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
#> Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
#> Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
#> Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
#> AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
#> Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
#> Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
#> Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
#> Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
#> Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
#> Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
#> Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
#> Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
#> Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

以上的笨办法就是提取、修改再拼接,如果想直接按某些条件定位重写而不是修改后拼接,有没有简洁点的方式呢?

    Hoas

    head(mtcars)
    #>                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
    #> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
    #> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
    #> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
    #> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
    #> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
    #> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
    
    tmp = mtcars[2:5, "cyl"]
    mtcars[2:5, "cyl"][which(tmp < 6)] = 2
    
    head(mtcars)
    #>                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
    #> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
    #> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
    #> Datsun 710        22.8   2  108  93 3.85 2.320 18.61  1  1    4    1
    #> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
    #> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
    #> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
      Hoas 更改标题为「【已解决】对数据框中满足某些条件的数据的重写

      Hoas 以上两种办法基本上是等价的,属于 David Robinson 说的 "Someone new to R doesn’t need three ways to access a column in a data frame" 的三种方法中的两种。只是我写的省略了中间变量。

      data.table 也比较简单:

      library(data.table)
      DT <- as.data.table(mtcars)
      DT[DT[, .I %in% 2:5 & cyl < 20], cyl := 2]
      DT

      你这个例子我没有想到比较简洁的极乐净土办法,希望能请教一下极乐净土高手。

        Liechi

        可以是可以,不过这么写不如baseR的代码容易想到。所以还是安心做baseR和tidyverse混打选手吧。

        library(dplyr)
        mtcars %>%
          mutate(cyl = ifelse((row_number() %in% 2:5 &cyl < 6), 2, cyl)) %>%
          head()
        #>    mpg cyl disp  hp drat    wt  qsec vs am gear carb
        #> 1 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
        #> 2 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
        #> 3 22.8   2  108  93 3.85 2.320 18.61  1  1    4    1
        #> 4 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
        #> 5 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
        #> 6 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

          tctcab Liechi Hoas 我突然发现我好像已经看不懂各位的代码了,我提供一个基于 Base R 的方案,我自认为我的代码可读性强的多,也更加简洁,为了以示区别我这里新添加一列 new_cyl,在这里你完全可以继续使用列名cyl

          transform(mtcars[2:5,], new_cyl = ifelse(cyl < 6,2,cyl))
                             mpg cyl disp  hp drat    wt  qsec vs am gear carb new_cyl
          Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4       6
          Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1       2
          Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1       6
          Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2       8

          希望大家不要动不动就跳极乐净土 <https://github.com/matloff/TidyverseSkeptic>

          至于可维护性和稳定性,咱们不妨定个五年或十年之约,看看谁的代码还能不经改动照样运行?我想我会完胜 😂

            tctcab 原来如此!我一直觉得极乐净土的工具不适合按位置选行来操作,不过这也是个办法;写成 1:length(mtcars$cyl) %in% 2:5应该也行。

            先前看到了 row_number 这个函数,不过一看说明书,上边赫然写着 row_number(): equivalent to rank(ties.method = "first")。这命名把我惊到了,于是未敢深究其详(现在知道了这好像是从 SQL 里借过来的名字)。

            有一点我不大明白,我把你代码里的 row_number() 换成 rank(ties.method = "first") 就不工作了。不知道这里的 row_number() 接受了管道递送过来的哪个数据?

            dapengde Cloud2016 我昨天刚写了一篇中文日志分析净土问题(唉,真是太复杂了,交战双方都有些失去理智)。待我把下一个 R 包整好了推上 CRAN 就来集中发布我这半年的日志。

              我觉得这里用 dplyr “不太直接”的原因是“行2到5”这个条件。我的第一反应是先 mutate 一个变量 index 表示行数,再用 ifelse 给 “cyl_new” 赋值,conditional on “index” 和 “cyl” 。

              Cloud2016 Hadley 大人开始帮 data.table 做网站搞推广了。以后 R 史家不知是否会引这条 issue,并注:从此,R 界虽依旧兄弟睨于墙,但外御欺辱了。

                Liechi
                好事!tidyverse系列的网站做得都比较好,都有非常好的示例可学,而data.table个人觉得虽然简洁但是没有那么直接,有些费脑筋,希望这次合作可以承袭tidyverse系列网站的作风,给 R 用户更多的选择和权衡。