Zip lub wyliczyć w R?

84

Jakie są odpowiedniki R dla tych wyrażeń listowych w języku Python:

[(i,j) for i,j in zip(index, Values)]
[(i,j) for i,j in enumerate(Values)]
[(i,j) for i,j in enumerate(range(10,20))]   %MWE, indexing or enumerating to 
                                            %keep up with the index, there may 
                                            %be some parameter to look this up

Przykład z wyjściem

>>> [(i,j) for i,j in enumerate(range(10,20))]
[(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), (7, 17), (8, 18), (9, 19)]

Rozwiązałem ten problem wcześniej z jakąś sztuczką w R, ale już nie pamiętam, pierwszym pomysłem było itertools -pkg, ale mam nadzieję, że znajdę bardziej idiomatyczny sposób robienia rzeczy.

hhh
źródło
2
jeśli mógłbyś podać mały, sprawdzony przykład dla tych z nas, którzy nie są zaznajomieni z Pythonem, może to zwiększyć populację potencjalnych odpowiedzi. Domyślam się, że ostatni toexpand.grid(i=10:20,j=10:20)
Ben Bolker,
@BenBolker: dodano wyjście - teraz jasne? To może być trudniejsze, ale logika jest ważna ...
hhh,
1
Zgadzam się z @DWin. Nierozsądne jest oczekiwanie mapowania jeden do jednego między strukturami danych w R i Pythonie. Jeśli potrzebujesz dobrych odpowiedzi, powinieneś określić, jak chcesz, aby wynik wyglądał w R, a nie w Pythonie.
joran
Przy okazji, oto zgrabny sposób na spakowanie i spłaszczenie dwóch list:as.vector(rbind(1:10, 11:20))
smci

Odpowiedzi:

47

Odpowiedź dla Pythona enumerate:

W R uporządkowana jest lista (patrz odpowiedź ). Zatem wszystko, czego potrzebujesz, to indeksować klucze (używając names()[i]) lub wartości (używając [[i]]).

Korzystanie seq_along(alternatywnie można to zrobić for(i in 1:length(mylist)){...}):

> mylist <- list('a'=10,'b'=20,'c'=30)
> for (i in seq_along(mylist)){
+   print(paste(i,names(mylist)[i],mylist[[i]]))
+ }
[1] "1 a 10"
[1] "2 b 20"
[1] "3 c 30"

Odpowiedź dla Pythona zip:

Zobacz jedną z powyższych odpowiedzi, aby naśladować listę krotek. Preferuję ramkę danych, jak pokazano w odpowiedzi BondedDust:

> x <- 1:3
> y <- 4:6
> data.frame(x=x, y=y)
  x y
1 1 4
2 2 5
3 3 6
Theja Tulabandhula
źródło
1
Kontynuując pierwszy przykład w drugim,data.frame(names=labels(mylist),values=unlist(mylist),row.names = 1:length(mylist))
Josiah Yoder,
pytanie o wydajność: czy wywoływanie nazwisk (mylista) [i] musi za każdym razem działać, czy jest to banalna operacja? Zastanawiam się, czy najlepiej byłoby przypisać go do name_list przed pętlą
markgalassi
42

Odbyły się dyskusje na temat rozumienia list w języku R, np. Tutaj lub tam . Hash pakiet oferuje nawet słownika jak struktury. Jednak, jak powiedzieli inni, trudno jest spróbować zmapować jedne udogodnienia językowe na inne (nawet jeśli to właśnie oferuje Porównanie języków programowania ) bez jasnego zrozumienia, do czego ma być przyzwyczajony. Na przykład mogę naśladować Pythonazip() w R w następujący sposób:

Pyton

In [1]: x = [1,2,3]
In [2]: y = [4,5,6]
In [3]: zip(x, y)
Out[3]: [(1, 4), (2, 5), (3, 6)]

R

> x <- 1:3
> y <- 4:6
> list(x, y)                     # gives a simple list
> as.list(paste(x, y))           # three tuples, as a list of characters
> mapply(list, x, y, SIMPLIFY=F) # gives a list of 3 tuples
> rbind(x, y)                    # gives a 2x3 matrix 

Jak widać, to naprawdę zależy od tego, co chcesz później zrobić z wynikiem.

chl
źródło
1
Myślę, że pytanie brzmi, czego używasz, gdy używasz zip w Pythonie. typowym zastosowaniem jest zrobienie listy z wieloma argumentami, więc mapply obsługuje to bezpośrednio.
seanv507
6
Właśnie mapplytego chcemy dla bezpośredniego analogu.
StephenBoesch
@javadba mapplyobejmuje najczęstszy przypadek użycia: zip, a następnie map.
Josiah Yoder
8

Inną opcją, która utworzy listę wektorów, jest użycie funkcji Map, jak widać tutaj @peterhurford: https://rdrr.io/github/peterhurford/funtools/src/R/zippers.R

> x <- 1:3
> y <- 4:6
> z <- 7:9
> Map(c, x, y, z)
[[1]]
[1] 1 4 7

[[2]]
[1] 2 5 8

[[3]]
[1] 3 6 9
wphampton
źródło
W Pythonie pierwotne wykorzystanie zip jest iteracja ciągu kilku wektorów / list: for xi, yi in zip(x, y): .... +1 za najbardziej eleganckie rozwiązanie, jakie do tej pory widziałem, aby to zrobić w R:for (xi.yi in Map(c, x, y)) { xi <- xi.yi[1]; yi <- xi.yi[2]; ... }
sgrubsmyon
6

Jeśli to jest reprezentacja macierzy w druku w języku Python, to następujący kod:

j <- 10:20
matrix(c(seq_along(j), j), ncol=2)
#------------
      [,1] [,2]
 [1,]    1   10
 [2,]    2   11
 [3,]    3   12
 [4,]    4   13
 [5,]    5   14
 [6,]    6   15
 [7,]    7   16
 [8,]    8   17
 [9,]    9   18
[10,]   10   19
[11,]   11   20

Wciąż pozostawiasz tych z nas, którzy nie są użytkownikami Pythona, w ciemności, jeśli chodzi o strukturę pożądanego wyniku. Używasz terminu „lista”, ale wynik sugeruje uporządkowany zestaw krotek.

Biorąc pod uwagę wskazówki @ chi, możemy również zasugerować użycie samej R-centrycznej struktury „dataframe”

x <- 1:3
y <- 4:6
dfrm <- data.frame(x=x, y=y)

... który ma elastyczność listy pod względem typów kolumn i funkcji dostępu do macierzy pod względem indeksowania wierszy i kolumn. Lub można użyć żądania hhh i utworzyć niejawnie indeksowane wartości wektora j 10:20, używając rownameswektora, który domyślnie zaczyna się od „1”, ale który można zmienić, aby stał się wektorem znakowym zaczynającym się od „0”

dfrm <- data.frame(j=10:20)
dfrm[3, ]
#[1] 12

 rownames(dfrm) <- 0:10
 dfrm["0",]
# [1] 10

Niestety, nieostrożni stwierdzą, że dfrm [0,] nie jest szczęśliwym wywołaniem, zwracającym wektor o długości 0.

IRTFM
źródło
+1 za eleganckie rozwiązanie. (Nie, to nie są macierze Pythona, ale jak już zgadłeś lista krotek .)
chl
4

Aby używać list składanych w stylu Pythona z wyliczeniami, takimi jak listy wyliczane, jednym ze sposobów jest zainstalowanie pakietu List-compention LC(opracowany w 2018 r.) I itertools (opracowany w 2015 r.).

Listy składane w R

LCPakiet znajdziesz tutaj .

install.packages("devtools")
devtools::install_github("mailund/lc")

Przykład

> library(itertools); library(lc)
> lc(paste(x$index, x$value), x=as.list(enumerate(rnorm(5))), )
[[1]]
[1] "1 -0.715651978438808"

[[2]]
[1] "2 -1.35430822605807"

[[3]]
[1] "3 -0.162872340884235"

[[4]]
[1] "4 1.42909760816254"

[[5]]
[1] "5 -0.880755983937781"

gdzie składnia programowania nie jest jeszcze tak czysta i dopracowana jak w Pythonie, ale działa funkcjonalnie, a jej pomoc przedstawia:

„Składnia jest następująca: lc (wyrażenie, listy, predykaty) gdzie wyrażenie jest wyrażeniem, które ma zostać ocenione dla wszystkich elementów na listach, gdzie listy to jedna lub więcej nazwanych list, gdzie są one określone przez nazwę i nazwę wyrażenia = list_expr i gdzie predykaty są wyrażeniami, które powinny być obliczane na wartość logiczną. Na przykład, aby uzyskać listę wszystkich liczb parzystych do kwadratu z listy x, możemy napisać lc (x ** 2, x = x, x% % 2 == 0). Wynikiem wywołania lc jest lista utworzona z wyrażeń w wyrażeniu dla wszystkich elementów na listach wejściowych, których predykaty są prawdziwe. "

gdzie zauważ, że możesz pozostawić predykaty puste, na przykład w powyższym przykładzie.

Narzędzia itertools i wyliczenia w stylu Pythona

Możesz użyć itertools R, które są bardzo podobne do itertools w Pythonie, dalej w Cran tutaj

library(itertools)

gdzie opisano

„Różne narzędzia do tworzenia iteratorów, wiele wzorowanych na funkcjach w module itertools języka Python i inne wzorowane na funkcjach z pakietu 'snow'”.

Przykład. wyliczenie

> for (a in as.list(enumerate(rnorm(5)))) { print(paste(a$index, "index:", a$value))}
[1] "1 index: 1.63314811372568"
[1] "2 index: -0.983865948988314"
[1] "3 index: -1.27096072277818"
[1] "4 index: 0.313193212706331"
[1] "5 index: 1.25226639725357"

Przykład. wyliczenie za pomocą ZIP

> for (h in as.list(izip(a=1:5, b=letters[1:5]))) { print(paste(h$a, "index:", h$b))}
[1] "1 index: a"
[1] "2 index: b"
[1] "3 index: c"
[1] "4 index: d"
[1] "5 index: e"
hhh
źródło
4

zipi enumeratenie są szczególnie trudne do wdrożenia w R:

#' zip(1:5,1:10)
zip <- function(...) {
  mapply(list, ..., SIMPLIFY = FALSE)
}

Enumerate jest łatwe do zdefiniowania pod względem zip:

#' enumerate(l=LETTERS)
enumerate <- function(...) {
  zip(ix=seq_along(..1), ...)
}

Ponieważ są to właściwe funkcje, możemy ich użyć, ...aby uczynić je dość elastycznymi i zwięzłymi, a także wykorzystać zachowanie mapply, takie jak recykling danych wejściowych i prawidłowe nazewnictwo wyników.

Neal Fultz
źródło
1
Te zostały dodane do stackoverflowpakietu, fwiw.
Neal Fultz
0
# similar to python. return a list of list. Short sequences get recycled.
zip <- function(...){ 
    all.list <- list(...)
    ele.names <- names(all.list)
    max.length <- max(sapply(all.list, length))
    lapply(0:(max.length - 1), function(i) {
        res <- lapply(all.list, function(l) l[i %% length(l) + 1]) 
        names(res) <- ele.names
        res
    })
}
Liść
źródło
Podaj opis działania tego bloku kodu.
Keivan Esbati
ta funkcja robi dokładnie to samo z „mapply (list, x, y, SIMPLIFY = F)”, które wskazał
@chl
0

Można to osiągnąć za pomocą dwóch instrukcji wklejania:

str1 <- paste(1:11, 10:20, sep=",", collapse='), (')
paste("(", str1, ")", sep = "")

Wyjście spodoba się następującym:

'(1,10), (2,11), (3,12), (4,13), (5,14), (6,15), (7,16), (8,17), (9,18), (10,19), (11,20)'
kravi
źródło
0

Dla Pythona odpowiednik „enumerate” w R. Przechowywanie wektorów na liście i iterowanie po nich z indeksem powinno działać dobrze.

vect1 <- c('A', 'B', 'C')
vect2 <- c('a', 'b', 'c')

# eqiv to zip values:
idx_list <- list(vect1, vect2)
idx_vect <- c(1:length(idx_list[[1]]))

for(i in idx_vect){
    x <- idx_list[[1]][i]
    j <- idx_list[[2]][i]
    print(c(i, x, j))
}

Wynik:

[1] "1" "A" "a"
[1] "2" "B" "b"
[1] "3" "C" "c"

R 'lista' to fajny bank do deponowania wektorów i przechowywania z indeksem.

Surya
źródło