2017-12-06 13 views
1

Edit: war es ein Tippfehler in meiner df Schöpfung, mit einer fehlenden _ auf dem letzten Wert von MediaName ; Das ist jetzt korrigiert.strsplit innerhalb dplyr Mit :: mutieren (ohne tibble :: data_frame) wirft "Evaluation Fehler: Nicht-Zeichen-Argument"

Ich möchte eine neue Variable TrialId in einem Datenrahmen als Teil des Wertes einer anderen Variablen MediaName erstellen, die auf den Wert einer dritten Variablen Phase abhängig, und dachte, ich könnte, dass innerhalb eines dplyr::mutatestrsplit und ifelse mit tun wie folgt :

library(dplyr) 

# Creating a simple data frame for the example 
df <- data.frame(Phase = c(rep("Familiarisation",8),rep("Test",3)), 
       MediaName = c("Flip_A1_G1","Reg_B2_S1","Reg_A2_G1","Flip_B1_S1", 
           "Reg_A1_G2","Flip_B2_S2","Reg_A2_G2","Flip_B1_S2", 
           "HC_A1L","TC_B1R","RC_BL_2R")) 

# Creating a new column 
df <- df %>% 
    mutate(TrialId = ifelse(Phase == "Familiarisation", 
          sapply(strsplit(MediaName, "_"), "[", 2), 
          sapply(strsplit(MediaName, "_"), "[", 1))) 

Das erwartete Ergebnis ist

> df$TrialId 
[1] "A1" "B2" "A2" "B1" "A1" "B2" "A2" "B1" "HC" "TC" "RC" 

jedoch gibt dies mir die folgende Fehlermeldung, weil ich glaube, die strsplit:

Error in mutate_impl(.data, dots) : 
    Evaluation error: non-character argument. 

Ich weiß aus this SO question, dass ich einfach mein Problem durch die Definition lösen kann, in diesem kleinen Beispiel, meinen Datenrahmen als tibble::data_frame, ohne zu wissen, warum dies das Problem löst. Ich kann nicht genau das tun, da in meinem tatsächlichen Code df kommt aus dem Lesen einer CSV-Datei (mit read.csv()). Ich habe gedacht, dass die Verwendung df <- df %>% as_tibble() %>% mutate(...) das Problem in ähnlicher Weise lösen würde, aber es nicht (warum?).

Gibt es eine Möglichkeit, tibble auch beim Lesen von Dateien zu verwenden? Oder gibt es einen anderen Weg zu erreichen, was ich tun muss, ohne strsplit vielleicht zu verwenden?

lese ich auch auf this other SO question, die Sie tidyr::separate verwenden können, aber es ist nicht genau das tun, was ich will, wie ich halten müssen entweder den ersten oder zweiten Wert auf den Wert von Phase abhängig.

+1

Vielleicht haben Sie 'Faktor' Klasse. Versuchen Sie, in 'character' zu konvertieren und machen Sie dann' ifelse', d. H. 'Df%>% mutate_all (als.Zeichen)%>% 'vor dem' muate' Aufruf – akrun

+0

Verdammt, das fühlte sich so offensichtlich an, dass ich es nicht einmal probiert habe, aber es funktioniert ... Ich bin immer noch daran interessiert zu verstehen, warum 'tibble :: data_frame' verwendet wird macht es aber nicht 'as_tibble' verwenden. –

+1

Der Hauptgrund ist, dass 'data_frame' standardmäßig' character' Klasse für alle diese nicht numerischen Spalten liefert, während 'data.frame' standardmäßig' stringsAsFactors = TRUE' ist. und da Sie 'as_tibble' verwenden, wird die durch 'data.frame' erzeugte Spaltenklasse nicht geändert. – akrun

Antwort

1

Das Problem, das aufgetreten ist, ist, weil die Zeichenfolge automatisch in factor konvertiert wurde, daher können Sie strsplit() nicht auf ein Objekt ohne Zeichenfolge anwenden. Meine Lösung wandelt einfach den MediaName in einen string Typ um.

require(dplyr)  
df <- df %>% 
     dplyr::mutate(MediaName = as.character(levels(df$MediaName))[df$MediaName]) %>% 
       dplyr::mutate(TrialId = ifelse(Phase == "Familiarisation", 
             sapply(strsplit(MediaName, "_"), "[", 2), 
             sapply(strsplit(MediaName, "_"), "[", 1))) 





solution<- c("A1", "B2", "A2", "B1", "A1", "B2", "A2", "B1", "HC", "TC", "RC") 
identical(solution, df$TrialId) 
[1] TRUE 
+0

Können Sie Ihre Antwort erweitern? Ich sehe nicht, wie man das ohne 'mutate' macht ... –

+0

@ArthurSpoon Ich präsentiere Ihnen keine Mutate-Lösung, aber meine Idee löst Ihr Problem. Ziehen Sie darüber hinaus die Verwendung von 'df $ MediaName <- as.character (Ebenen (df $ MediaName)) [df $ MediaName]' in Betracht, da Sie 'MediaName' als 'Faktor' speichern und für 'strsplit' eine Zeichenkette benötigen !! – Seymour

+0

Ja, ich weiß das, @akrun hat diesen Punkt früher gemacht. Ich nahm an, dass sich "strsplit" genauso verhalten würde wie "grepl", indem er einen Faktor akzeptiert, wenn er es tatsächlich nicht tut (und die Dokumentation sagt es, hoppla). –

2

können Sie versuchen:

library(tidyverse) 
# your first data 
df_old <- data.frame(Phase = c(rep("Familiarisation",8),rep("Test",3)), 
       MediaName = c("Flip_A1_G1","Reg_B2_S1","Reg_A2_G1","Flip_B1_S1", 
           "Reg_A1_G2","Flip_B2_S2","Reg_A2_G2","Flip_B1_S2", 
           "HC_A1L","TC_B1R","RC_BL2R")) 
df_old %>% 
    separate(MediaName, into=letters[1:3], sep="_", fill = "left", remove = FALSE) %>% 
    select(Phase, MediaName, TrialId=b) 
      Phase MediaName TrialId 
1 Familiarisation Flip_A1_G1  A1 
2 Familiarisation Reg_B2_S1  B2 
3 Familiarisation Reg_A2_G1  A2 
4 Familiarisation Flip_B1_S1  B1 
5 Familiarisation Reg_A1_G2  A1 
6 Familiarisation Flip_B2_S2  B2 
7 Familiarisation Reg_A2_G2  A2 
8 Familiarisation Flip_B1_S2  B1 
9    Test  HC_A1L  HC 
10   Test  TC_B1R  TC 
11   Test RC_BL2R  RC 

Es ist eine hartcodierte Lösung die bereitgestellten Beispieldaten nach. Trennen Sie durch "_", wenn es nur zwei statt drei "_" only s von der linken Seite sind. Wählen Sie abschließend die gewünschten Spalten aus.

bearbeiten

Mit Ihrem neuen Daten ist es etwas komplizierter. aber Sie können versuchen:

df %>% 
    add_column(MediaName_keep=df$MediaName) %>% 
    group_by(MediaName_keep) %>% 
    separate_rows(MediaName, sep="_") %>% 
    mutate(n=1:n()) %>% 
    filter((Phase == "Familiarisation" & n == 2) | (Phase == "Test" & n == 1)) %>% 
    select(Phase, MediaName=MediaName_keep, TrialId=MediaName) 
# A tibble: 11 x 3 
# Groups: MediaName [11] 
      Phase MediaName TrialId 
      <fctr>  <fctr> <chr> 
1 Familiarisation Flip_A1_G1  A1 
2 Familiarisation Reg_B2_S1  B2 
3 Familiarisation Reg_A2_G1  A2 
4 Familiarisation Flip_B1_S1  B1 
5 Familiarisation Reg_A1_G2  A1 
6 Familiarisation Flip_B2_S2  B2 
7 Familiarisation Reg_A2_G2  A2 
8 Familiarisation Flip_B1_S2  B1 
9   Test  HC_A1L  HC 
10   Test  TC_B1R  TC 
11   Test RC_BL_2R  RC 

Die Idee ist die gleiche. Getrennt, aber zu diesem Zeitpunkt addieren Sie und zählen Sie die neuen Reihen durch MediaName_keep, dann filtern Sie entsprechend Ihren Notwendigkeiten.

+0

Ich mag diese nette Antwort wirklich, aber es tut es nicht Funktioniert wegen eines Tippfehlers in meiner 'df'-Definition (jetzt korrigiert): Manchmal habe ich zwei' _' in 'MediaName', wenn' Phase == "Test" ', aber immer noch den ersten, nicht den zweiten Wert benötigt in diesen Fällen ...:/ –

+0

@ArthurSpoon siehe meine Änderungen – Jimbou

Verwandte Themen