Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dplyr grouped mutations als iteratoren #34

Open
phish108 opened this issue May 16, 2021 · 1 comment
Open

dplyr grouped mutations als iteratoren #34

phish108 opened this issue May 16, 2021 · 1 comment
Assignees
Labels
Blog Blog Beiträge

Comments

@phish108
Copy link
Member

phish108 commented May 16, 2021

Mutate sieht erst einmal wie eine map Funktion aus. Tatsächlich handelt es sich um eine Vektoroperation. D.h. es wird immer der gesamte Vektor an eine Funktion übergeben.

bsp.

library(tidyverse)

tibble(ab = c("a", "b")) %>%
   mutate(ergebnis = str_c(ab, collapse = ""))

Das Ergebnis ist

ab ergebnis
a ab
b ab

Das hat alle möglichen Vorteile. Leider muss diese Verarbeitung bei Funktionen berücksichtigt werden. Das kann bei selbstgeschrieben Funktionen aber ein Problem sein, wenn diese Funktionen einzelne Werte erwarten. In solchen Fällen führt mutate immer wieder zu Problemen mit kryptischen Fehlermeldungen über zu langen Vektoren usw.

Im Beispiel verwende ich str_c(). Ohne collapse = funktioniert diese Funktion wie erwartet, weil die Funktion speziell für die Verwendung in mutate und summarise entwickelt wurde. Sie führt eine Vektor-Wert-Unterscheidung durch. Für die meisten Fälle funktioniert das ganz gut, aber leider nicht immer.

Im Internet finden wir eine Menge Lösungen, die in solchen Fällen auf die map()-Funktionen der purrr-Bibliothek ausweichen. Das ist natürlich unschön. Eine andere Variante bietet die rowwise()
-Funktion von dplyr. Diese Funktion hat aber mehrere Probleme, so dass die Entwickler von dplyr empfehlen, diese Funktion nicht mehr zu verwenden. Eines dieser Probleme ist die Bearbeitungsgeschwindigkeit und ein zweites ist die unflexible Handhabung im dplyr-Programmiermodell.

Ich habe mich gefragt, wie wir innerhalb des dplyr-Modells zeilenweise Transformationen erzwingen können, ohne map-Funktionen bemühen zu müssen. Die Lösung greift auf das Konzept der Iteratoren zurück. Dieses Konzept ist in der modernen Programmierung wichtig, um Iterationen umzusetzen. Eine Iteration ähnelt im Ergebnis einer Schleife - nur eben ohne eine Schleife mit for, while oder eben map()/reduce(). Stattdessen definieren wir einen Iterator, der die Schritte angibt und arbeiten diesen Iterator ab.

Mit dplyr funktioniert das so:

stichprobe %>%
    mutate(
        iterator = 1:n()
    ) %>%
    group_by(iterator) %>%
    mutate(
         # Zeilenweise Transformationen
    )

Diese Technik weist jedem Datensatz einen eindeutigen Wert zu. Dieser. Wert steht hier im Vektor iterator. Anschliessend wird die Stichprobe entlang dieses Vektors gruppiert. Weil der Vektor für jeden Datensatz einen eindeutigen Wert erzeugt hat, besteht jede Gruppe genau aus einem Datensatz. Die anschliessende Transformation mit mutate() erhält damit jeweils immer nur Vektoren mit einem Element und nicht n()-Elementen ohne die Gruppierung.

Mit dieser Technik lassen sich auch verschachtelte Operationen mit mehreren Iteratoren realisieren. Dabei müssen wir beachten, dass der innerste Iterator immer genau einen Datensatz umfasst und wir nach aussen immer mehr Datensätze zusammenfassen. Für diese Technik verwenden wir ebenfalls Gruppierungen. Weil wir aber für äussere Iteratoren normalerweise Variablenbezogene Bedingungen verwenden, müssen wir keine zusätzlichen Iteratoren einführen, sondern können uns auf den Zeileniterator konzentrieren.

@phish108 phish108 added the Blog Blog Beiträge label May 16, 2021
@phish108 phish108 self-assigned this May 16, 2021
@phish108
Copy link
Member Author

phish108 commented May 16, 2021

anderes Beispiel mit unerwarteten Ergebnis:

tibble(x = c("a", "b", "a"), y = list(list("c", "d"), list("e","f"), list("g", "h")) %>%
    mutate(xy = str_c(x, y %>% unlist, collapse = "", sep = ""))

| x <chr> | y <list> | xy <chr> |
| --- | --- | --- |
| a | c, d | acbdaeafbgah |
| b | e, f  | acbdaeafbgah |
| a | g, h | acbdaeafbgah |

Mit Variablen Gruppierung:

tibble(x = c("a", "b", "a"), y = list(list("c", "d"), list("e","f"), list("g", "h")) %>%
     group_by(x) %>%
    mutate(xy = str_c(x, y %>% unlist, collapse = "", sep = "") )

| x <chr> | y <list> | xy <chr> |
| --- | --- | --- |
| a | c, d | acadagah |
| b | e, f  | bebf |
| a | g, h | acadagah |

mit Iterator

tibble(x = c("a", "b", "a"), y = list(list("c", "d"), list("e","f"), list("g", "h")) %>%
    mutate(iterator = 1:n()) %>%
    group_by(iterator) %>%
    mutate(xy = str_c(x, y %>% unlist, collapse = "", sep = ""))

| x <chr> | y <list> | xy <chr> |
| --- | --- | --- |
| a | c, d | acad |
| b | e, f  | bebf |
| a | g, h | agah |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Blog Blog Beiträge
Projects
None yet
Development

No branches or pull requests

1 participant