Wählen Sie Datenrahmenspalten dynamisch mit $ und einem Zeichenwert aus


121

Ich habe einen Vektor mit verschiedenen Spaltennamen und möchte in der Lage sein, jeden von ihnen zu durchlaufen, um diese Spalte aus einem data.frame zu extrahieren. Betrachten Sie beispielsweise den Datensatz mtcarsund einige Variablennamen, die in einem Zeichenvektor gespeichert sind cols. Wenn ich versuche, eine Variable aus mtcarseiner dynamischen Teilmenge von colsauszuwählen, funktioniert nichts davon

cols <- c("mpg", "cyl", "am")
col <- cols[1]
col
# [1] "mpg"

mtcars$col
# NULL
mtcars$cols[1]
# NULL

Wie kann ich diese dazu bringen, die gleichen Werte wie zurückzugeben?

mtcars$mpg

Wie kann ich außerdem alle Spalten durchlaufen cols, um die Werte in einer Art Schleife zu erhalten?

for(x in seq_along(cols)) {
   value <- mtcars[ order(mtcars$cols[x]), ]
}

Antworten:


181

Sie können diese Art von Teilmenge nicht mit machen $. Im Quellcode ( R/src/main/subset.c) heißt es:

/ * Der Operator $ subset.
Wir müssen sicher sein, nur das erste Argument zu bewerten.
Das zweite Symbol ist ein Symbol, das abgeglichen und nicht ausgewertet werden muss.
* /

Zweites Argument? Was?! Sie müssen erkennen , dass $, wie alles andere in R (einschließlich zum Beispiel (, +, ^etc) ist eine Funktion, die ausgewertet Argumente und nimmt. df$V1könnte umgeschrieben werden als

`$`(df , V1)

oder in der Tat

`$`(df , "V1")

Aber...

`$`(df , paste0("V1") )

... zum Beispiel wird niemals funktionieren, noch wird irgendetwas anderes, das zuerst im zweiten Argument bewertet werden muss. Sie dürfen nur eine Zeichenfolge übergeben, die niemals ausgewertet wird.

Verwenden Sie stattdessen [(oder [[wenn Sie nur eine einzelne Spalte als Vektor extrahieren möchten).

Beispielsweise,

var <- "mpg"
#Doesn't work
mtcars$var
#These both work, but note that what they return is different
# the first is a vector, the second is a data.frame
mtcars[[var]]
mtcars[var]

Sie können die Bestellung ohne Schleifen ausführen, indem Sie do.callden Aufruf an erstellen order. Hier ist ein reproduzierbares Beispiel unten:

#  set seed for reproducibility
set.seed(123)
df <- data.frame( col1 = sample(5,10,repl=T) , col2 = sample(5,10,repl=T) , col3 = sample(5,10,repl=T) )

#  We want to sort by 'col3' then by 'col1'
sort_list <- c("col3","col1")

#  Use 'do.call' to call order. Seccond argument in do.call is a list of arguments
#  to pass to the first argument, in this case 'order'.
#  Since  a data.frame is really a list, we just subset the data.frame
#  according to the columns we want to sort in, in that order
df[ do.call( order , df[ , match( sort_list , names(df) ) ]  ) , ]

   col1 col2 col3
10    3    5    1
9     3    2    2
7     3    2    3
8     5    1    3
6     1    5    4
3     3    4    4
2     4    3    4
5     5    1    4
1     2    5    5
4     5    3    5

Hat sich diese Situation in den letzten Jahren geändert?
Dunois

4

Wenn ich das richtig verstehe, haben Sie einen Vektor mit Variablennamen und möchten jeden Namen durchlaufen und Ihren Datenrahmen danach sortieren. In diesem Fall sollte dieses Beispiel eine Lösung für Sie darstellen. Das Hauptproblem bei Ihnen (das vollständige Beispiel ist nicht vollständig, daher bin ich mir nicht sicher, was Sie sonst noch vermissen könnten) ist, dass es order(Q1_R1000[,parameter[X]])stattdessen sein sollte order(Q1_R1000$parameter[X]), da Parameter ein externes Objekt ist, das einen Variablennamen enthält, der einer direkten Spalte entgegengesetzt ist Ihres Datenrahmens (was, wenn das $angemessen wäre).

set.seed(1)
dat <- data.frame(var1=round(rnorm(10)),
                   var2=round(rnorm(10)),
                   var3=round(rnorm(10)))
param <- paste0("var",1:3)
dat
#   var1 var2 var3
#1    -1    2    1
#2     0    0    1
#3    -1   -1    0
#4     2   -2   -2
#5     0    1    1
#6    -1    0    0
#7     0    0    0
#8     1    1   -1
#9     1    1    0
#10    0    1    0

for(p in rev(param)){
   dat <- dat[order(dat[,p]),]
 }
dat
#   var1 var2 var3
#3    -1   -1    0
#6    -1    0    0
#1    -1    2    1
#7     0    0    0
#2     0    0    1
#10    0    1    0
#5     0    1    1
#8     1    1   -1
#9     1    1    0
#4     2   -2   -2

4

Die Verwendung von dplyr bietet eine einfache Syntax zum Sortieren der Datenrahmen

library(dplyr)
mtcars %>% arrange(gear, desc(mpg))

Es kann nützlich sein, die hier gezeigte NSE-Version zu verwenden, um die Sortierliste dynamisch erstellen zu können

sort_list <- c("gear", "desc(mpg)")
mtcars %>% arrange_(.dots = sort_list)

Was bedeutet NSE hier?
Discipulus

1
@discipulus nicht standardmäßige Bewertung; Es dient zum Arbeiten mit verzögerten Ausdrücken, um den Code dynamisch mit Zeichenfolgen zu erstellen, anstatt ihn fest zu codieren. Weitere Informationen finden Sie hier: cran.r-project.org/web/packages/lazyeval/vignettes/…
Manotheshark

1

Eine andere Lösung ist die Verwendung von #get:

> cols <- c("cyl", "am")
> get(cols[1], mtcars)
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

0

Hatte ein ähnliches Problem aufgrund einiger CSV-Dateien, die verschiedene Namen für dieselbe Spalte hatten.
Dies war die Lösung:

Ich habe eine Funktion geschrieben, um den ersten gültigen Spaltennamen in einer Liste zurückzugeben, und dann diese ...

# Return the string name of the first name in names that is a column name in tbl
# else null
ChooseCorrectColumnName <- function(tbl, names) {
for(n in names) {
    if (n %in% colnames(tbl)) {
        return(n)
    }
}
return(null)
}

then...

cptcodefieldname = ChooseCorrectColumnName(file, c("CPT", "CPT.Code"))
icdcodefieldname = ChooseCorrectColumnName(file, c("ICD.10.CM.Code", "ICD10.Code"))

if (is.null(cptcodefieldname) || is.null(icdcodefieldname)) {
        print("Bad file column name")
}

# Here we use the hash table implementation where 
# we have a string key and list value so we need actual strings,
# not Factors
file[cptcodefieldname] = as.character(file[cptcodefieldname])
file[icdcodefieldname] = as.character(file[icdcodefieldname])
for (i in 1:length(file[cptcodefieldname])) {
    cpt_valid_icds[file[cptcodefieldname][i]] <<- unique(c(cpt_valid_icds[[file[cptcodefieldname][i]]], file[icdcodefieldname][i]))
}

0

Wenn Sie eine Spalte mit einem bestimmten Namen auswählen möchten, tun Sie dies einfach

A=mtcars[,which(conames(mtcars)==cols[1])]
#and then
colnames(mtcars)[A]=cols[1]

Sie können es auch in umgekehrter Schleife ausführen, um einen dynamischen Namen hinzuzufügen. Wenn beispielsweise A ein Datenrahmen und xyz eine Spalte ist, die als x bezeichnet werden soll, gefällt mir dies

A$tmp=xyz
colnames(A)[colnames(A)=="tmp"]=x

Auch dies kann auch in einer Schleife hinzugefügt werden


Ich weiß nicht, warum negativ gewählt, aber es funktioniert und einfache Möglichkeit, anstatt komplizierte Funktionen zu schreiben
Makarand Kulkarni


-1

zu spät .. aber ich denke ich habe die Antwort -

Hier ist mein Beispiel Study.df Datenrahmen -

   >study.df
   study   sample       collection_dt other_column
   1 DS-111 ES768098 2019-01-21:04:00:30         <NA>
   2 DS-111 ES768099 2018-12-20:08:00:30   some_value
   3 DS-111 ES768100                <NA>   some_value

Und dann -

> ## Selecting Columns in an Given order
> ## Create ColNames vector as per your Preference
> 
> selectCols <- c('study','collection_dt','sample')
> 
> ## Select data from Study.df with help of selection vector
> selectCols %>% select(.data=study.df,.)
   study       collection_dt   sample
1 DS-111 2019-01-21:04:00:30 ES768098
2 DS-111 2018-12-20:08:00:30 ES768099
3 DS-111                <NA> ES768100
> 
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.