Jak poprawnie używać list w R?

Krótkie tło: wiele (większość? współczesne języki programowania, powszechnie stosowane, mają co najmniej kilka wspólnych ADT [abstrakcyjnych typów danych], w szczególności

  • String (Sekwencja złożona ze znaków)

  • Lista (uporządkowany zbiór wartości), oraz

  • Typ oparty na mapach (nieuporządkowana tablica mapująca Klucze do wartości)

W języku programowania R, pierwszy dwa są zaimplementowane odpowiednio jako character i vector.

Kiedy zacząłem uczyć się R, dwie rzeczy były oczywiste niemal od samego początku: {[6] } jest najważniejszym typem danych w R (ponieważ jest klasą nadrzędną dla R data.frame), a po drugie, po prostu nie mogłem zrozumieć, jak one działają, przynajmniej nie na tyle dobrze, aby używać ich poprawnie w moim kodzie.

Po pierwsze, wydawało mi się, że typ danych r list jest prostą implementacją mapy ADT (dictionary w Pythonie, NSMutableDictionary w Objective C, hash w Perlu i Ruby, object literal W Javascript i tak dalej).

Na przykład, tworzysz je tak, jak w słowniku Pythona, przekazując pary klucz-wartość konstruktorowi (który w Pythonie jest dict Nie list):

x = list("ev1"=10, "ev2"=15, "rv"="Group 1")

I uzyskujesz dostęp do elementów listy R, tak jak w słowniku Pythona, np. x['ev1']. Podobnie, możesz pobrać tylko 'klucze' lub po prostu 'wartości' przez:

names(x)    # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"

unlist(x)   # fetch just the 'values' of an R list
#   ev1       ev2        rv 
#  "10"      "15" "Group 1" 

x = list("a"=6, "b"=9, "c"=3)  

sum(unlist(x))
# [1] 18

Ale R lists są również w przeciwieństwie do inne mapy typu ADTs (spośród języków, których i tak się nauczyłem). Domyślam się, że jest to konsekwencja początkowej specyfikacji dla S, czyli zamiaru zaprojektowania danych/statystyk DSL [języka specyficznego dla domeny] od podstaw.

trzy znaczące różnice między R listS I typami mapowania w innych językach w powszechnym użyciu (np. Python, Perl, JavaScript):

pierwszy, lists W R są uporządkowane zbiór, podobnie jak wektory, mimo że wartości są keyed (tzn. klucze mogą być dowolną wartością hashable, a nie tylko sekwencyjnymi liczbami całkowitymi). Prawie zawsze typ danych mapowania w innych językach to unordered .

drugi, lists może być zwrócone z funkcji, nawet jeśli nigdy nie przekazałeś w list Kiedy wywołałeś funkcję, i nawet jeśli funkcja, która zwróciła list nie zawiera konstruktora (jawnego) list (oczywiście możesz z tym w praktyce przez zawinięcie zwracanego wyniku w wywołanie do unlist):

x = strsplit(LETTERS[1:10], "")     # passing in an object of type 'character'

class(x)                            # returns 'list', not a vector of length 2
# [1] list

Atrzecia osobliwa cecha R lists: nie wydaje się, aby mogły być członkami innego ADT, a jeśli spróbujesz to zrobić, podstawowy kontener zostanie zmuszony do list. Np.,

x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)

class(x)
# [1] list

Moim zamiarem nie jest krytykowanie języka ani sposobu jego udokumentowania; podobnie, nie sugeruję, że jest coś złego w strukturze danych list lub w jaki sposób się zachowuje. All I ' m after jest do poprawienia jest moje zrozumienie, jak one działają, więc mogę poprawnie używać ich w moim kodzie.

Oto rodzaje rzeczy, które chciałbym lepiej zrozumieć:

  • Jakie są reguły, które określają, kiedy wywołanie funkcji zwróci list (np. wyrażenie strsplit wypowiedziane powyżej)?

  • Jeśli nie przypisuję jawnie nazw list (np. list(10,20,30,40)), czy domyślne nazwy są tylko sekwencyjnymi liczbami całkowitymi zaczynającymi się od 1? (Zakładam, ale jestem daleki od pewnego że odpowiedź brzmi tak, w przeciwnym razie nie bylibyśmy w stanie zmusić tego typu list do wektora w/ a wywołania do unlist.)

  • Dlaczego te dwa różne operatory, [] i [[]], zwracają ten sam wynik?

    x = list(1, 2, 3, 4)

    Oba wyrażenia zwracają "1":

    x[1]

    x[[1]]

  • Dlaczego te dwa wyrażenia Nie zwracają to samo wynik?

    x = list(1, 2, 3, 4)

    x2 = list(1:4)

Proszę nie wskazywać mi dokumentacji R(?list, R-intro)--przeczytałem go uważnie i nie pomaga mi to odpowiedzieć na pytania, które wypowiedziałem tuż powyżej.

(ostatnio dowiedziałem się i zacząłem używać pakietu R (dostępnego na CRAN) o nazwie hash który implementuje konwencjonalne zachowanie typu map poprzez klasę S4; z pewnością mogę polecić to Paczka.)

Author: Gregor, 2010-01-12

11 answers

Aby odpowiedzieć na ostatnią część twojego pytania, ponieważ to naprawdę wskazuje na różnicę między a list i vector W R:

Dlaczego te dwa wyrażenia nie zwracają tego samego wyniku?

X = lista(1, 2, 3, 4); x2 = list (1: 4)

Lista może zawierać dowolną inną klasę jako każdy element. Więc możesz mieć listę, gdzie pierwszy element jest wektorem znaków, drugi jest ramką danych, itp. W takim przypadku utworzyłeś dwie różne listy. x ma cztery wektory, każdy o długości 1. x2 mA 1 wektor długości 4:

> length(x[[1]])
[1] 1
> length(x2[[1]])
[1] 4

Więc są to zupełnie inne listy.

Listy R są bardzo podobne do mapy hashowej struktury danych, ponieważ każda wartość indeksu może być powiązana z dowolnym obiektem. Oto prosty przykład listy, która zawiera 3 różne klasy (w tym funkcję):

> complicated.list <- list("a"=1:4, "b"=1:3, "c"=matrix(1:4, nrow=2), "d"=search)
> lapply(complicated.list, class)
$a
[1] "integer"
$b
[1] "integer"
$c
[1] "matrix"
$d
[1] "function"

Biorąc pod uwagę, że ostatnim elementem jest funkcja wyszukiwania, mogę ją nazwać tak:

> complicated.list[["d"]]()
[1] ".GlobalEnv" ...

Jako finał komentarz na ten temat: należy zauważyć, że {[9] } to tak naprawdę lista (z dokumentacji data.frame):

Ramka danych jest listą zmiennych o tej samej liczbie wierszy z unikalnymi nazwami wierszy, podanymi danymi klasy"".frame "'

Dlatego kolumny w data.frame mogą mieć różne typy danych, podczas gdy kolumny w macierzy nie mogą. Jako przykład, tutaj staram się stworzyć macierz z liczbami i znakami:

> a <- 1:4
> class(a)
[1] "integer"
> b <- c("a","b","c","d")
> d <- cbind(a, b)
> d
 a   b  
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
[4,] "4" "d"
> class(d[,1])
[1] "character"

Uwaga Jak nie mogę zmienić typu danych w pierwszym kolumny do liczb, ponieważ druga kolumna ma znaki:

> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"
 132
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-17 08:50:55

Jeśli chodzi o Twoje pytania, pozwól mi zająć się nimi w kolejności i podać kilka przykładów:

1) lista jest zwracana, jeśli i kiedy Instrukcja return dodaje ją. Consider

 R> retList <- function() return(list(1,2,3,4)); class(retList())
 [1] "list"
 R> notList <- function() return(c(1,2,3,4)); class(notList())
 [1] "numeric"
 R> 

2) nazwy po prostu nie są ustawione:

R> retList <- function() return(list(1,2,3,4)); names(retList())
NULL
R> 

3) nie zwracają tego samego. Twój przykład daje

R> x <- list(1,2,3,4)
R> x[1]
[[1]]
[1] 1
R> x[[1]]
[1] 1

Gdzie x[1] zwraca pierwszy element x -- który jest taki sam jak x. Każdy Skalar jest wektorem długości jedynki. Z drugiej strony x[[1]] zwraca pierwszy element listy.

4) W końcu te dwa elementy różnią się między sobą, tworząc odpowiednio listę zawierającą cztery Skalary i listę z jednym elementem (który jest wektorem czterech elementów).

 57
Author: Dirk Eddelbuettel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-12 20:02:30

Wystarczy wziąć podzbiór twoich pytań:

Ten artykuł na temat indeksowania porusza kwestię różnicy między [] i [[]].

W skrócie [[]] wybiera pojedynczy element z listy i [] zwraca listę wybranych elementów. W twoim przykładzie x = list(1, 2, 3, 4)' Pozycja 1 jest pojedynczą liczbą całkowitą, ale x[[1]] zwraca pojedynczą liczbę 1, A x[1] zwraca listę z tylko jedną wartością.

> x = list(1, 2, 3, 4)
> x[1]
[[1]]
[1] 1

> x[[1]]
[1] 1
 34
Author: JD Long,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-12 17:35:20

Jednym z powodów, dla których listy działają tak, jak one (uporządkowane), jest zaspokojenie potrzeby uporządkowanego kontenera, który może zawierać dowolny typ w dowolnym węźle, czego wektory nie robią. Listy są ponownie używane do różnych celów w R, w tym tworząc bazę data.frame, która jest listą wektorów dowolnego typu (ale tej samej długości).

Dlaczego te dwa wyrażenia nie zwracają tego samego wyniku?

x = list(1, 2, 3, 4); x2 = list(1:4)

Aby dodać do odpowiedzi @ Shane, jeśli chcesz uzyskać taki sam wynik, try:

x3 = as.list(1:4)

, który wymusza wektor 1:4 na listę.

 12
Author: Alex Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-12 18:19:46

Aby dodać jeszcze jeden punkt do tego:

R ma strukturę danych równoważną dict Pythona w hash pakiecie . Możesz przeczytać o tym w ten wpis na blogu z grupy Open Data . Oto prosty przykład:

> library(hash)
> h <- hash( keys=c('foo','bar','baz'), values=1:3 )
> h[c('foo','bar')]
<hash> containing 2 key-value pairs.
  bar : 2
  foo : 1

Pod względem użyteczności, Klasa hash jest bardzo podobna do listy. Ale wydajność jest lepsza dla dużych zbiorów danych.

 11
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 19:14:40

Mówisz:

Dla innego, listy mogą być zwracane z funkcji, mimo że nigdy przekazane na liście, gdy zadzwoniłeś do funkcja, a mimo to funkcja nie zawiera konstruktora listy, np.,

x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x)
# => 'list'

I chyba sugerujesz, że to jest problem(?). Jestem tutaj, aby powiedzieć, dlaczego to nie problem : -). Twój przykład jest nieco prosty, ponieważ kiedy wykonujesz podział łańcuchów, masz listę z elementami o długości 1 elementu, więc wiesz że x[[1]] jest tym samym co unlist(x)[1]. Ale co, jeśli wynik strsplit zwróci wyniki o różnej długości w każdym pojemniku. Po prostu zwrócenie wektora (a listy) w ogóle nie zadziała.

Na przykład:

stuff <- c("You, me, and dupree",  "You me, and dupree",
           "He ran away, but not very far, and not very fast")
x <- strsplit(stuff, ",")
xx <- unlist(strsplit(stuff, ","))

W pierwszym przypadku (x : który zwraca listę), możesz określić, jaka była druga "część" trzeciego łańcucha, np.: x[[3]][2]. Jak możesz zrobić to samo używając xx Teraz, gdy wyniki zostały "rozwikłane" (unlist-ed)?

 8
Author: Steve Lianoglou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-27 17:33:13
x = list(1, 2, 3, 4)
x2 = list(1:4)
all.equal(x,x2)

Nie jest tym samym, ponieważ 1: 4 jest tym samym co c (1,2,3,4). Jeśli chcesz, żeby były takie same to:

x = list(c(1,2,3,4))
x2 = list(1:4)
all.equal(x,x2)
 5
Author: JeremyS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-13 05:28:34

Odnośnie wektorów i koncepcji hash/array z innych języków:

  1. Wektory są atomami R. np. rpois(1e4,5) (5 liczb losowych), numeric(55) (Długość-55 wektorów zerowych nad podwójnymi) i character(12) (12 pustych ciągów), wszystkie są "podstawowe".

  2. Albo listy, albo wektory mogą mieć names.

    > n = numeric(10)
    > n
     [1] 0 0 0 0 0 0 0 0 0 0
    > names(n)
    NULL
    > names(n) = LETTERS[1:10]
    > n
    A B C D E F G H I J 
    0 0 0 0 0 0 0 0 0 0
    
  3. Wektory wymagają, aby wszystko było tego samego typu danych. Zobacz to:

    > i = integer(5)
    > v = c(n,i)
    > v
    A B C D E F G H I J           
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    > class(v)
    [1] "numeric"
    > i = complex(5)
    > v = c(n,i)
    > class(v)
    [1] "complex"
    > v
       A    B    C    D    E    F    G    H    I    J                          
    0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i
    
  4. Listy mogą zawierać różne typy danych, co widać w inne odpowiedzi i samo pytanie OP.

Widziałem języki (ruby, javascript), w których "tablice" mogą zawierać zmienne typy danych, ale na przykład w C++ "tablice" muszą być tymi samymi typami danych. Jeśli masz numeric(1e6) znasz jego rozmiar i położenie każdego elementu a priori; jeśli rzecz może zawierać "Flying Purple People Eaters" w jakimś nieznanym kawałku, to musisz dokładnie przeanalizować rzeczy, aby poznać podstawowe fakty na temat tego, co się dzieje. to.

Niektóre standardowe operacje R mają również większy sens, gdy typ jest gwarantowany. Na przykład cumsum(1:9) ma sens, podczas gdy cumsum(list(1,2,3,4,5,'a',6,7,8,9)) nie ma, bez gwarancji, że typ będzie podwójny.


Co do drugiego pytania:]}

Listy mogą być zwracane z funkcji, nawet jeśli nigdy nie trafiłeś na Listę po wywołaniu funkcji

Funkcje zwracają różne typy danych, niż są wprowadzane przez cały czas. plot zwraca fabułę mimo nie bierze fabuły jako wkładu. Arg zwraca numeric, mimo że przyjmuje complex. Itd.

(a co do strsplit: kod źródłowy to tutaj .)

 1
Author: isomorphismes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-07 16:38:48

Jeśli to pomoże, mam tendencję do wyobrażania "list" w R jako "rekordów"w innych językach pre-OO:

  • nie przyjmują żadnych założeń co do nadrzędnego typu (a raczej rodzaju wszystkich możliwych rekordów o dowolnej arytmetyce i nazwach pól jest dostępny).
  • ich pola mogą być anonimowe (wtedy uzyskujesz do nich dostęp według ścisłego porządku definicji).

Nazwa "rekord" koliduje ze standardowym znaczeniem "rekordów" (aka rows) w języku bazodanowym i być może dlatego ich nazwa zasugerował: as lists (of fields)

 1
Author: Francisco J. Valverde Albacete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-03 09:39:24

Alhough to dość stare pytanie muszę powiedzieć, że dotyka dokładnie wiedzy, której brakowało mi podczas pierwszych kroków w R - czyli jak wyrazić dane w dłoni jako obiekt w R lub jak wybrać z istniejących obiektów. Nie jest łatwo początkującemu R myśleć "w pudełku R" od samego początku.

Więc sam zacząłem używać kul poniżej, które pomogły mi wiele dowiedzieć się, jaki obiekt użyć do jakich danych, i zasadniczo wyobrazić sobie rzeczywiste użycie świata.

Choć ja nie podanie dokładnych odpowiedzi na pytanie krótki tekst poniżej może pomóc czytelnikowi, który dopiero zaczął od R i zadaje pytania.

  • Wektor atomowy ... Nazwałem to "sekwencją" dla siebie, bez kierunku, po prostu sekwencją tych samych typów. [ podzbiory.
  • Wektor ... Sekwencja o jednym kierunku od 2D, [ podzbiorów.
  • Matrix ... zbiór wektorów o tej samej długości tworzących wiersze lub kolumny, [ podzbiory według wierszy i kolumn lub według sekwencji.
  • Tablice ... matryce warstwowe tworzące 3D
  • Dataframe ... tabela 2D jak w Excelu, gdzie mogę sortować, dodawać lub usuwać wiersze lub kolumny lub tworzyć arit. operacje z nimi, dopiero po pewnym czasie naprawdę uznałem, że dataframe jest sprytną implementacją list, Gdzie mogę podzlecić za pomocą [ przez wiersze i kolumny, ale nawet za pomocą [[.
  • lista ... aby pomóc sobie pomyślałem o liście od tree structure gdzie [i] wybiera i zwraca całe gałęzie, a [[i]] zwraca element z gałęzi. A ponieważ jest to tree like structure, można nawet użyć index sequence do adresowania każdego pojedynczego liścia na bardzo złożonym list za pomocą jego [[index_vector]]. Listy mogą być proste lub bardzo złożone i mogą łączyć ze sobą różne typy obiektów w jedną.

Więc dla lists możesz skończyć z więcej sposobów, jak wybrać leaf w zależności od sytuacji, jak w poniższym przykładzie.

l <- list("aaa",5,list(1:3),LETTERS[1:4],matrix(1:9,3,3))
l[[c(5,4)]] # selects 4 from matrix using [[index_vector]] in list
l[[5]][4] # selects 4 from matrix using sequential index in matrix
l[[5]][1,2] # selects 4 from matrix using row and column in matrix
Ten sposób myślenia bardzo mi pomógł.
 1
Author: Petr Matousu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-20 14:54:23

Dlaczego te dwa różne operatory, [ ] i [[ ]], zwracają ten sam wynik?

x = list(1, 2, 3, 4)
  1. [ ] zapewnia operację ustawienia podrzędnego. Ogólnie podzbiór dowolnego obiektu będzie miał ten sam typ co oryginalny obiekt. Dlatego x[1] zawiera listę. Podobnie x[1:2] jest podzbiorem oryginalnej listy, dlatego jest to lista. Ex.

    x[1:2]
    
    [[1]] [1] 1
    
    [[2]] [1] 2
    
  2. [[ ]] służy do wyodrębniania elementu z listy. x[[1]] jest poprawna i wyodrębnij pierwszy element z listy. x[[1:2]] is not valid as [[ ]] nie zapewnia ustawienia podrzędnego, takiego jak [ ].

     x[[2]] [1] 2 
    
    > x[[2:3]] Error in x[[2:3]] : subscript out of bounds
    
 1
Author: HariG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-17 08:57:05