2017-02-01 4 views
2

Ich möchte eine Funktion, die in einer Spalte als Zeichenfolge gespeichert ist, auf einen Wert in einer anderen Spalte mit dplyr anwenden. Ich habe mehrere Dinge mit mutate_ und einem .dots Argument versucht, aber ich bin jetzt stecken.R dplyr: Verwenden Sie die Funktion als Zeichenfolge in einer Spalte in der nächsten Spalte

library(lubridate) 
library(dplyr) 

df <- data.frame(date=as.POSIXct('2017/01/01 12:34') + 1:10*123456, 
       fun=rep(c('minute','hour','day','month','year'),2)) 

Eingang:

> df 
        date fun 
1 2017-01-02 22:51:36 minute 
2 2017-01-04 09:09:12 hour 
3 2017-01-05 19:26:48 day 
4 2017-01-07 05:44:24 month 
5 2017-01-08 16:02:00 year 
6 2017-01-10 02:19:36 minute 
7 2017-01-11 12:37:12 hour 
8 2017-01-12 22:54:48 day 
9 2017-01-14 09:12:24 month 
10 2017-01-15 19:30:00 year 

Ausgang:

    date fun res 
1 2017-01-02 22:51:36 minute 51 
2 2017-01-04 09:09:12 hour 9 
3 2017-01-05 19:26:48 day 5 
4 2017-01-07 05:44:24 month 1 
5 2017-01-08 16:02:00 year 2017 
6 2017-01-10 02:19:36 minute 19 
7 2017-01-11 12:37:12 hour 12 
8 2017-01-12 22:54:48 day 12 
9 2017-01-14 09:12:24 month 1 
10 2017-01-15 19:30:00 year 2017 

Antwort

2

Sie können mit do.call dass versuchen, aber Sie haben rowwise zu verwenden:

library("dplyr") 
library("lubridate") 

df <- data.frame(
    date = as.POSIXct('2017/01/01 12:34') + 1:10*123456, 
    fun = rep(c('minute','hour','day','month','year'),2), 
    stringsAsFactors = FALSE 
) 

df %>% rowwise() %>% mutate(res = as.character(do.call(fun, list(date)))) 
+0

Obwohl ich auch alle anderen Antworten mag, kommt mir das am nächsten, was ich anfangs dachte. – Wietze314

2

Wir verwenden mapply

df$res <- mapply(function(x,y) get(x)(y), as.character(df$fun), df$date) 
df$res 
#[1] 51 9 5 1 2017 19 12 12 1 2017 

Eine weitere Option ist data.table

library(data.table) 
setDT(df)[, res := as.integer(get(as.character(fun))(date)), 1:nrow(df)] 
df 
#     date fun res 
#1: 2017-01-02 22:51:36 minute 51 
#2: 2017-01-04 09:09:12 hour 9 
#3: 2017-01-05 19:26:48 day 5 
#4: 2017-01-07 05:44:24 month 1 
#5: 2017-01-08 16:02:00 year 2017 
#6: 2017-01-10 02:19:36 minute 19 
#7: 2017-01-11 12:37:12 hour 12 
#8: 2017-01-12 22:54:48 day 12 
#9: 2017-01-14 09:12:24 month 1 
#10: 2017-01-15 19:30:00 year 2017 

HINWEIS: Ohne zusätzliche Anstrengungen, um Nachschlagetabellen bei der Schaffung von

+0

Gibt es eine Möglichkeit, dies in einer 'dplyr' Kette zu erreichen? – Wietze314

+0

@ Wietze314 Sie können 'map' aus' purrr' verwenden, das auch Teil von 'tidyverse' ist. – akrun

+0

Vielen Dank für die Antwort. Ich interessiere mich dafür, mehr über purrr zu lernen (und data.table, das ich die ganze Zeit aufschiebe). – Wietze314

5

Ein Weg, Ich könnte mir vorstellen, dass ich eine Nachschlagetabelle erstelle und dann das richtige Ausgabeformat unter Verwendung von match

erhalte

Obwohl dies eine Warnmeldung gibt, aber immer noch die Ausgabe korrekt ist.

Wenn wir dies benötigen in einer dplyr Kette

library(dplyr) 
df %>% 
    mutate(res = format(date, format = y[match(df$fun, x)])) 


#     date fun res 
#1 2017-01-02 22:51:36 minute 51 
#2 2017-01-04 09:09:12 hour 09 
#3 2017-01-05 19:26:48 day 05 
#4 2017-01-07 05:44:24 month 01 
#5 2017-01-08 16:02:00 year 2017 
#6 2017-01-10 02:19:36 minute 19 
#7 2017-01-11 12:37:12 hour 12 
#8 2017-01-12 22:54:48 day 12 
#9 2017-01-14 09:12:24 month 01 
#10 2017-01-15 19:30:00 year 2017 
+1

Ich mag diesen. Vektorisierung ftw. –

+1

Danke für die Antwort. Es ist auch eine sehr elegante Lösung – Wietze314

1

Um eine volle tidyverse hier gehen, können wir purrr die invoke_map()-Funktion verwenden. Es enthält eine Liste von Funktionen und eine Liste von Listen mit Parameterwerten, die für jede Funktion verwendet werden. Es ist wie eine vektorisierte do.call().

Die lubridate Funktionen in df$fun erwarten ein Argument x, so brauchen wir eine Liste von Listen mit jedem Datum als x benanntes Element gespeichert zu erstellen. Wir können eine Listenspalte von Datenrahmen erstellen, indem wir die Datumsspalte kopieren und nest() verwenden.

df2 <- df %>% 
    mutate(x = date) %>% 
    tidyr::nest(x, .key = "params") 
df2 
#> # A tibble: 10 × 3 
#>     date fun   params 
#>     <dttm> <chr>   <list> 
#> 1 2017-01-02 22:51:36 minute <tibble [1 × 1]> 
#> 2 2017-01-04 09:09:12 hour <tibble [1 × 1]> 
#> 3 2017-01-05 19:26:48 day <tibble [1 × 1]> 
#> 4 2017-01-07 05:44:24 month <tibble [1 × 1]> 
#> 5 2017-01-08 16:02:00 year <tibble [1 × 1]> 
#> 6 2017-01-10 02:19:36 minute <tibble [1 × 1]> 
#> 7 2017-01-11 12:37:12 hour <tibble [1 × 1]> 
#> 8 2017-01-12 22:54:48 day <tibble [1 × 1]> 
#> 9 2017-01-14 09:12:24 month <tibble [1 × 1]> 
#> 10 2017-01-15 19:30:00 year <tibble [1 × 1]> 

Jedes Element in der Spalte params ist ein Datenrahmen mit einer Spalte x. Das ist unsere Liste von Listen.

df2$params[1] 
#> [[1]] 
#> # A tibble: 1 × 1 
#>      x 
#>     <dttm> 
#> 1 2017-01-02 22:51:36 

Mit unseren beiden Listen können wir invoke_map() verwenden und eine Liste der Ergebnisse.

str(purrr::invoke_map(df2$fun, df2$params)) 
#> List of 10 
#> $ : int 51 
#> $ : int 9 
#> $ : int 5 
#> $ : num 1 
#> $ : num 2017 
#> $ : int 19 
#> $ : int 12 
#> $ : int 12 
#> $ : num 1 
#> $ : num 2017 

Aber weil wir wissen, dass diese Funktionen nur einen numerischen Wert zurückgeben jeden, können wir die Ergebnisse in einem schönen Vektor mit invoke_map_dbl() bekommen.

df2 %>% 
    mutate(res = purrr::invoke_map_dbl(fun, params)) %>% 
    select(-params) 
#> # A tibble: 10 × 3 
#>     date fun res 
#>     <dttm> <chr> <dbl> 
#> 1 2017-01-02 22:51:36 minute 51 
#> 2 2017-01-04 09:09:12 hour  9 
#> 3 2017-01-05 19:26:48 day  5 
#> 4 2017-01-07 05:44:24 month  1 
#> 5 2017-01-08 16:02:00 year 2017 
#> 6 2017-01-10 02:19:36 minute 19 
#> 7 2017-01-11 12:37:12 hour 12 
#> 8 2017-01-12 22:54:48 day 12 
#> 9 2017-01-14 09:12:24 month  1 
#> 10 2017-01-15 19:30:00 year 2017 
Verwandte Themen