Een functie toepassen op elke rij van een tabel met dplyr?

Bij het werken met plyrvond ik het vaak handig om adplyte gebruiken voor scalaire functies die ik op elke rij moet toepassen.

bijv.

data(iris)
library(plyr)
head(
     adply(iris, 1, transform , Max.Len= max(Sepal.Length,Petal.Length))
    )
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len
1          5.1         3.5          1.4         0.2  setosa     5.1
2          4.9         3.0          1.4         0.2  setosa     4.9
3          4.7         3.2          1.3         0.2  setosa     4.7
4          4.6         3.1          1.5         0.2  setosa     4.6
5          5.0         3.6          1.4         0.2  setosa     5.0
6          5.4         3.9          1.7         0.4  setosa     5.4

Nu ik dplyrmeer gebruik, vraag ik me af of er een nette/natuurlijke manier is om dit te doen? Omdat dit NIETis wat ik wil:

library(dplyr)
head(
     mutate(iris, Max.Len= max(Sepal.Length,Petal.Length))
    )
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len
1          5.1         3.5          1.4         0.2  setosa     7.9
2          4.9         3.0          1.4         0.2  setosa     7.9
3          4.7         3.2          1.3         0.2  setosa     7.9
4          4.6         3.1          1.5         0.2  setosa     7.9
5          5.0         3.6          1.4         0.2  setosa     7.9
6          5.4         3.9          1.7         0.4  setosa     7.9

Antwoord 1, autoriteit 100%

Vanaf dplyr 0.2 (denk ik) is rowwise()geïmplementeerd, dus het antwoord op dit probleem wordt:

iris %>% 
  rowwise() %>% 
  mutate(Max.Len= max(Sepal.Length,Petal.Length))

Niet rowwisealternatief

Vijf jaar (!) later krijgt dit antwoord nog steeds veel verkeer. Sinds het werd gegeven, wordt rowwisesteeds vaker niet aanbevolen, hoewel veel mensen het intuïtief lijken te vinden. Doe jezelf een plezier en doorloop Jenny Bryan’s Rij-georiënteerde workflows in R met het properemateriaal om dit onderwerp goed onder de knie te krijgen.

De meest eenvoudige manier die ik heb gevonden is gebaseerd op een van Hadley’s voorbeelden met behulp van pmap:

iris %>% 
  mutate(Max.Len= purrr::pmap_dbl(list(Sepal.Length, Petal.Length), max))

Met deze aanpak kun je een willekeurig aantal argumenten geven aan de functie (.f) in pmap.

pmapis een goede conceptuele benadering omdat het het feit weerspiegelt dat wanneer je rijgewijze bewerkingen uitvoert, je eigenlijk werkt met tuples uit een lijst met vectoren (de kolommen in een dataframe).


Antwoord 2, autoriteit 11%

De idiomatische benadering is het creëren van een op de juiste manier gevectoriseerde functie.

Rlevert pmaxwat hier geschikt is, maar het biedt ook Vectorizeals een wrapper voor mapplyom kunt u een gevectoriseerde willekeurige versie van een willekeurige functie maken.

library(dplyr)
# use base R pmax (vectorized in C)
iris %>% mutate(max.len = pmax(Sepal.Length, Petal.Length))
# use vectorize to create your own function
# for example, a horribly inefficient get first non-Na value function
# a version that is not vectorized
coalesce <- function(a,b) {r <- c(a[1],b[1]); r[!is.na(r)][1]}
# a vectorized version
Coalesce <- Vectorize(coalesce, vectorize.args = c('a','b'))
# some example data
df <- data.frame(a = c(1:5,NA,7:10), b = c(1:3,NA,NA,6,NA,10:8))
df %>% mutate(ab =Coalesce(a,b))

Houd er rekening mee dat het implementeren van vectorisatie in C / C++ sneller zal zijn, maar er is geen magicPony-pakket dat de functie voor u zal schrijven.


Antwoord 3, autoriteit 10%

Je moet per rij groeperen:

iris %>% group_by(1:n()) %>% mutate(Max.Len= max(Sepal.Length,Petal.Length))

Dit is wat de 1deed in adply.


Antwoord 4

Zoiets?

iris$Max.Len <- pmax(iris$Sepal.Length, iris$Petal.Length)

Antwoord 5

Naast het geweldige antwoord van @alexwhan, houd er rekening mee dat u ungroup()moet gebruiken om bijwerkingen te voorkomen. Dit komt omdat rowwise()een groeperingsbewerking is.

iris %>%
    rowwise() %>%
    mutate(Max.Len = max(Sepal.Length, Petal.Length))

geeft je:

  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
 1          5.1         3.5          1.4         0.2 setosa      5.1
 2          4.9         3            1.4         0.2 setosa      4.9
 3          4.7         3.2          1.3         0.2 setosa      4.7
 4          4.6         3.1          1.5         0.2 setosa      4.6
 5          5           3.6          1.4         0.2 setosa      5  
 6          5.4         3.9          1.7         0.4 setosa      5.4
 7          4.6         3.4          1.4         0.3 setosa      4.6
 8          5           3.4          1.5         0.2 setosa      5  
 9          4.4         2.9          1.4         0.2 setosa      4.4
10          4.9         3.1          1.5         0.1 setosa      4.9

Laten we nu aannemen dat u door moet gaan met de dplyr-pipe om een ​​leadtoe te voegen aan Max.Len:

iris %>%
    rowwise() %>%
    mutate(Max.Len = max(Sepal.Length, Petal.Length)) %>%
    mutate(Lead.Max.Len = lead(Max.Len))

Dit levert het volgende op:

  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len Lead.Max.Len
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>        <dbl>
 1          5.1         3.5          1.4         0.2 setosa      5.1           NA
 2          4.9         3            1.4         0.2 setosa      4.9           NA
 3          4.7         3.2          1.3         0.2 setosa      4.7           NA
 4          4.6         3.1          1.5         0.2 setosa      4.6           NA
 5          5           3.6          1.4         0.2 setosa      5             NA
 6          5.4         3.9          1.7         0.4 setosa      5.4           NA
 7          4.6         3.4          1.4         0.3 setosa      4.6           NA
 8          5           3.4          1.5         0.2 setosa      5             NA
 9          4.4         2.9          1.4         0.2 setosa      4.4           NA
10          4.9         3.1          1.5         0.1 setosa      4.9           NA

NA‘s worden geproduceerd als bijwerking. Dit kan worden gecorrigeerd met ungroup():

iris %>%
    rowwise() %>%
    mutate(Max.Len = max(Sepal.Length, Petal.Length)) %>%
    ungroup() %>%
    mutate(Lead.Max.Len = lead(Max.Len))

Dit levert de gewenste uitvoer op:

  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len lead.max.len
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>        <dbl>
 1          5.1         3.5          1.4         0.2 setosa      5.1          4.9
 2          4.9         3            1.4         0.2 setosa      4.9          4.7
 3          4.7         3.2          1.3         0.2 setosa      4.7          4.6
 4          4.6         3.1          1.5         0.2 setosa      4.6          5  
 5          5           3.6          1.4         0.2 setosa      5            5.4
 6          5.4         3.9          1.7         0.4 setosa      5.4          4.6
 7          4.6         3.4          1.4         0.3 setosa      4.6          5  
 8          5           3.4          1.5         0.2 setosa      5            4.4
 9          4.4         2.9          1.4         0.2 setosa      4.4          4.9
10          4.9         3.1          1.5         0.1 setosa      4.9          5.4

Antwoord 6

Voor de volledigheid ga ik de code van deze gebruikerwijzigen van de vergeten antwoord(en misschien wel het beste antwoord) van de vraag: Som over meerdere kolommen op. En pas het toe op uw probleem:

iris %>%
  mutate(max = select(.,c('Sepal.Length','Petal.Length')) %>% 
  apply(1, max, na.rm=TRUE))

Het resultaat wordt verwacht. Geaccepteerd antwoord zei dat rowwise steeds vaker niet wordt aanbevolen, en toepassen is basis R. U hoeft geen extra pakket zoals purrr te importeren.

U kunt de functie Apply() gebruiken met max, min, sum, mediaan, mean. Het is dus erg handig en eenvoudig.

Other episodes