Chcę przypisać wiele zmiennych w jednym wierszu w R. Czy można zrobić coś takiego?
values # initialize some vector of values
(a, b) = values[c(2,4)] # assign a and b to values at 2 and 4 indices of 'values'
Zwykle chcę przypisać około 5-6 zmiennych w jednym wierszu, zamiast mieć wiele wierszy. Czy jest alternatywa?
list($a, $b) = array(1, 2)
? To byłoby miłe! +1.vassign
sugestia poniżej jest bliska ... :)X <- list();X[c('a','b')] <- values[c(2,4)]
. OK, nie przypisujesz ich w obszarze roboczym, ale ładnie przechowujesz je razem na liście. Wolałbym to zrobić w ten sposób.Odpowiedzi:
Świetna odpowiedź na blogu Walcząc z problemami
Jest to zaczerpnięte stamtąd, z bardzo niewielkimi modyfikacjami.
KORZYSTANIE Z NASTĘPUJĄCYCH TRZECH FUNKCJI (plus jedna pozwalająca na tworzenie list o różnych rozmiarach)
# Generic form '%=%' = function(l, r, ...) UseMethod('%=%') # Binary Operator '%=%.lbunch' = function(l, r, ...) { Envir = as.environment(-1) if (length(r) > length(l)) warning("RHS has more args than LHS. Only first", length(l), "used.") if (length(l) > length(r)) { warning("LHS has more args than RHS. RHS will be repeated.") r <- extendToMatch(r, l) } for (II in 1:length(l)) { do.call('<-', list(l[[II]], r[[II]]), envir=Envir) } } # Used if LHS is larger than RHS extendToMatch <- function(source, destin) { s <- length(source) d <- length(destin) # Assume that destin is a length when it is a single number and source is not if(d==1 && s>1 && !is.null(as.numeric(destin))) d <- destin dif <- d - s if (dif > 0) { source <- rep(source, ceiling(d/s))[1:d] } return (source) } # Grouping the left hand side g = function(...) { List = as.list(substitute(list(...)))[-1L] class(List) = 'lbunch' return(List) }
Następnie do wykonania:
Zgrupuj lewą stronę używając nowej funkcji
g()
Prawa strona powinna być wektorem lub listą Użyj nowo utworzonego operatora binarnego%=%
# Example Call; Note the use of g() AND `%=%` # Right-hand side can be a list or vector g(a, b, c) %=% list("hello", 123, list("apples, oranges")) g(d, e, f) %=% 101:103 # Results: > a [1] "hello" > b [1] 123 > c [[1]] [1] "apples, oranges" > d [1] 101 > e [1] 102 > f [1] 103
Przykład korzystania z list o różnych rozmiarach:
Dłuższa lewa strona
g(x, y, z) %=% list("first", "second") # Warning message: # In `%=%.lbunch`(g(x, y, z), list("first", "second")) : # LHS has more args than RHS. RHS will be repeated. > x [1] "first" > y [1] "second" > z [1] "first"
Dłuższa prawa strona
g(j, k) %=% list("first", "second", "third") # Warning message: # In `%=%.lbunch`(g(j, k), list("first", "second", "third")) : # RHS has more args than LHS. Only first2used. > j [1] "first" > k [1] "second"
źródło
Złożyłem zeallota pakietu R, aby rozwiązać ten właśnie problem. zeallot zawiera operator (
%<-%
) do rozpakowywania, wielokrotnego i niszczenia przypisania. LHS wyrażenia przypisania jest budowany przy użyciu wywołań funkcjic()
. RHS wyrażenia przypisania może być dowolnym wyrażeniem, które zwraca lub jest wektorem, listą, listą zagnieżdżoną, ramką danych, ciągiem znaków, obiektem daty lub obiektami niestandardowymi (zakładając, że istniejedestructure
implementacja).Oto pierwsze pytanie przerobione przy użyciu zeallota (najnowsza wersja, 0.0.5).
library(zeallot) values <- c(1, 2, 3, 4) # initialize a vector of values c(a, b) %<-% values[c(2, 4)] # assign `a` and `b` a #[1] 2 b #[1] 4
Więcej przykładów i informacji można znaleźć w winiecie na pakiet .
źródło
Rozważ użycie funkcji zawartych w podstawowym R.
Na przykład utwórz 1-wierszową ramkę danych (powiedzmy
V
) i zainicjuj w niej swoje zmienne. Teraz możesz przypisać do wielu zmiennych narazV[,c("a", "b")] <- values[c(2, 4)]
, wywołać każdą z nich po nazwie (V$a
) lub używać wielu z nich jednocześnie (values[c(5, 6)] <- V[,c("a", "b")]
).Jeśli zrobisz się leniwy i nie będziesz chciał wywoływać zmiennych z ramki danych, możesz
attach(V)
(chociaż ja osobiście nigdy tego nie robię).# Initialize values values <- 1:100 # V for variables V <- data.frame(a=NA, b=NA, c=NA, d=NA, e=NA) # Assign elements from a vector V[, c("a", "b", "e")] = values[c(2,4, 8)] # Also other class V[, "d"] <- "R" # Use your variables V$a V$b V$c # OOps, NA V$d V$e
źródło
oto mój pomysł. Prawdopodobnie składnia jest dość prosta:
`%tin%` <- function(x, y) { mapply(assign, as.character(substitute(x)[-1]), y, MoreArgs = list(envir = parent.frame())) invisible() } c(a, b) %tin% c(1, 2)
daje tak:
> a Error: object 'a' not found > b Error: object 'b' not found > c(a, b) %tin% c(1, 2) > a [1] 1 > b [1] 2
nie jest to jednak dobrze przetestowane.
źródło
c(c, d) %tin% c(1, 2) + 3
(=> C = 1, d = 1, zwraca numeryczne ( 0)) można uznać za zaskakujące.Potencjalnie niebezpieczną
assign
opcją (o ile używanie jest ryzykowne) byłobyVectorize
assign
:assignVec <- Vectorize("assign",c("x","value")) #.GlobalEnv is probably not what one wants in general; see below. assignVec(c('a','b'),c(0,4),envir = .GlobalEnv) a b 0 4 > b [1] 4 > a [1] 0
Albo przypuszczam, że możesz ręcznie wektoryzować go ręcznie za pomocą własnej funkcji, używając
mapply
prawdopodobnie rozsądnej wartości domyślnej dlaenvir
argumentu. Na przykładVectorize
zwróci funkcję o takich samych właściwościach środowiskaassign
, co w tym przypadkunamespace:base
, lub możesz po prostu ustawićenvir = parent.env(environment(assignVec))
.źródło
Jak wyjaśnili inni, wygląda na to, że nic nie jest wbudowane. ... ale możesz zaprojektować
vassign
funkcję w następujący sposób:vassign <- function(..., values, envir=parent.frame()) { vars <- as.character(substitute(...())) values <- rep(values, length.out=length(vars)) for(i in seq_along(vars)) { assign(vars[[i]], values[[i]], envir) } } # Then test it vals <- 11:14 vassign(aa,bb,cc,dd, values=vals) cc # 13
Jedną rzeczą do rozważenia jest jednak to, jak postępować w przypadkach, w których podajesz np. 3 zmienne i 5 wartości lub odwrotnie. Tutaj po prostu powtarzam (lub skracam) wartości, aby miały taką samą długość jak zmienne. Może ostrzeżenie byłoby rozsądne. Ale pozwala na:
vassign(aa,bb,cc,dd, values=0) cc # 0
źródło
...()
, co dla mnie wygląda jak czarna magia ...?...()
to ekstremalna czarna magia ;-). Tak się składa, że kiedy "wywołanie funkcji"...()
zostaje zastąpione, staje się listą par, do której można przekazaćas.character
i voila, argumenty otrzymano jako łańcuchy ...envir=parent.frame()
- I możesz określić, na przykład,envir=globalenv()
jeśli chcesz.`vassign<-` <- function (..., envir = parent.frame (), value)
i tak dalej. Wydaje się jednak, że pierwszy obiekt do przypisania musiałby już istnieć. Jakieś pomysły?list2env(setNames(as.list(rep(2,5)), letters[1:5]), .GlobalEnv)
Spełniło moje zadanie, tj. Przypisanie pięciu dwójek do pierwszych pięciu liter.
źródło
https://stat.ethz.ch/R-manual/R-devel/library/base/html/list2env.html :
list2env( list( a=1, b=2:4, c=rpois(10,10), d=gl(3,4,LETTERS[9:11]) ), envir=.GlobalEnv )
źródło
Miałem ostatnio podobny problem i oto moja próba użycia
purrr::walk2
purrr::walk2(letters,1:26,assign,envir =parent.frame())
źródło
Jeśli Twoim jedynym wymaganiem jest posiadanie jednej linii kodu, co powiesz na:
> a<-values[2]; b<-values[4]
źródło
Obawiam się, że eleganckie rozwiązanie, którego szukasz (lubisz
c(a, b) = c(2, 4)
), niestety nie istnieje. Ale nie poddawaj się, nie jestem pewien! Najbliższe rozwiązanie, jakie przychodzi mi do głowy, to:attach(data.frame(a = 2, b = 4))
lub jeśli niepokoją Cię ostrzeżenia, wyłącz je:
attach(data.frame(a = 2, b = 4), warn = F)
Ale przypuszczam, że nie jesteś zadowolony z tego rozwiązania, ja też nie byłbym ...
źródło
R> values = c(1,2,3,4) R> a <- values[2]; b <- values[3]; c <- values[4] R> a [1] 2 R> b [1] 3 R> c [1] 4
źródło
Inna wersja z rekurencją:
let <- function(..., env = parent.frame()) { f <- function(x, ..., i = 1) { if(is.null(substitute(...))){ if(length(x) == 1) x <- rep(x, i - 1); stopifnot(length(x) == i - 1) return(x); } val <- f(..., i = i + 1); assign(deparse(substitute(x)), val[[i]], env = env); return(val) } f(...) }
przykład:
> let(a, b, 4:10) [1] 4 5 6 7 8 9 10 > a [1] 4 > b [1] 5 > let(c, d, e, f, c(4, 3, 2, 1)) [1] 4 3 2 1 > c [1] 4 > f [1] 1
Moja wersja:
let <- function(x, value) { mapply( assign, as.character(substitute(x)[-1]), value, MoreArgs = list(envir = parent.frame())) invisible() }
przykład:
> let(c(x, y), 1:2 + 3) > x [1] 4 > y [1]
źródło
Łącząc niektóre odpowiedzi podane tutaj + trochę soli, co powiesz na to rozwiązanie:
assignVec <- Vectorize("assign", c("x", "value")) `%<<-%` <- function(x, value) invisible(assignVec(x, value, envir = .GlobalEnv)) c("a", "b") %<<-% c(2, 4) a ## [1] 2 b ## [1] 4
Użyłem tego, aby dodać sekcję R tutaj: http://rosettacode.org/wiki/Sort_three_variables#R
Uwaga: działa tylko w przypadku przypisywania zmiennych globalnych (takich jak
<<-
). Jeśli istnieje lepsze, bardziej ogólne rozwiązanie, proszę. powiedz mi w komentarzach.źródło