Krótkie tło: Wiele (najbardziej?) Współczesnych języków programowania w powszechnym użyciu ma co najmniej garstkę ADT [abstrakcyjnych typów danych], w szczególności:
ciąg (sekwencja złożona ze znaków)
lista (uporządkowany zbiór wartości) oraz
typ oparty na mapie (nieuporządkowana tablica odwzorowująca klucze na wartości)
W języku programowania R pierwsze dwa są realizowane odpowiednio jako character
i vector
.
Kiedy zacząłem uczyć się R, dwie rzeczy były oczywiste prawie od samego początku: list
jest to najważniejszy typ danych w R (ponieważ jest to klasa nadrzędna dla R data.frame
), a po drugie, po prostu nie mogłem zrozumieć, w jaki sposób działały nie wystarczająco dobrze, aby używać ich poprawnie w moim kodzie.
Po pierwsze, wydawało mi się, że list
typ danych R był prostą implementacją mapy ADT ( dictionary
w Pythonie, NSMutableDictionary
w Celu 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ść do konstruktora (co dict
nie jest w Pythonie list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
I uzyskujesz dostęp do elementów Listy R, tak jak do słownika Python, np x['ev1']
. Podobnie można pobrać tylko „klucze” lub tylko „wartości” poprzez:
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 list
są również inne niż inne ADT typu mapowego (spośród języków, których i tak się nauczyłem). Domyślam się, że jest to konsekwencja początkowej specyfikacji S, tj. Zamiaru zaprojektowania od podstaw danych DSL danych / statystyk [język specyficzny dla domeny].
trzy znaczące różnice między R list
i typami odwzorowań w innych językach w powszechnym użyciu (np. Python, Perl, JavaScript):
po pierwsze , list
s w R są kolekcją uporządkowaną , podobnie jak wektory, nawet jeśli wartości są kluczowane (tzn. klucze mogą być dowolnymi wartościami mieszającymi, a nie tylko kolejnymi liczbami całkowitymi). Prawie zawsze typ danych mapowania w innych językach jest nieuporządkowany .
po drugie , list
s mogą zostać zwrócone z funkcji, nawet jeśli nigdy nie przeszedłeś, list
kiedy wywołałeś funkcję, i nawet jeśli funkcja, która zwróciła list
nie zawiera (jawnego) list
konstruktora (Oczywiście możesz sobie z tym poradzić poprzez: zawijanie zwróconego 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
Trzecia cecha szczególna od R: list
s: to nie wydaje się, że mogą być członkami innego ADT, a jeśli staramy się robić to pierwotny pojemnik jest zmuszany do list
. Na przykład,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
nie zamierzam tu krytykować języka ani sposobu jego dokumentowania; podobnie nie sugeruję, że coś jest nie tak ze list
strukturą danych lub z jej zachowaniem. Chcę tylko poprawić, rozumiem, w jaki sposób działają, dzięki czemu mogę poprawnie używać ich w kodzie.
Oto rzeczy, które chciałbym lepiej zrozumieć:
Jakie reguły określają, kiedy wywołanie funkcji zwróci
list
(np.strsplit
Wyrażenie wyszczególnione powyżej)?Jeśli nie przypisuję jawnie nazw do
list
(np.list(10,20,30,40)
), Czy domyślne nazwy są tylko liczbami całkowitymi zaczynającymi się od 1? (Zakładam, ale nie jestem pewien, czy odpowiedź brzmi tak, w przeciwnym razie nie bylibyśmy w stanie zmusić tego typulist
do wektora z wywołaniemunlist
.)Dlaczego te dwa różne podmioty,
[]
i[[]]
przywróć sam wynik?x = list(1, 2, 3, 4)
oba wyrażenia zwracają „1”:
x[1]
x[[1]]
dlaczego te dwa wyrażenia nie zwracają tego samego wyniku?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Proszę nie kierować mnie do Dokumentacji R ( ?list
, R-intro
) - Przeczytałem ją uważnie i nie pomaga mi to w odpowiedzi na pytania, które przytoczyłem powyżej.
(na koniec niedawno się nauczyłem i zacząłem używać pakietu R (dostępnego w CRAN), hash
który implementuje konwencjonalne zachowanie typu mapy za pośrednictwem klasy S4; z pewnością mogę polecić ten pakiet.)
x = list(1, 2, 3, 4)
, oba NIE zwracają tego samego wyniku:,x[1]
ix[[1]]
. Pierwszy zwraca listę, a drugi zwraca wektor numeryczny. Przewijając poniżej, wydaje mi się, że Dirk był jedynym respondentem, który poprawnie odpowiedział na to pytanie.list
w R nie jest jak skrót. Mam jeszcze jedną, którą uważam za wartą odnotowania.list
w R może mieć dwóch członków o tej samej nazwie odniesienia. Zastanów się, żeobj <- c(list(a=1),list(a=2))
jest poprawny i zwraca listę z dwiema nazwanymi wartościami „a”. W tym przypadku wywołanie forobj["a"]
zwróci tylko pierwszy pasujący element listy. Możesz uzyskać zachowanie podobne (może identyczne) do skrótu z tylko jednym elementem na nazwy, do których się odwołujesz, używając środowisk w R. np.x <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
Odpowiedzi:
Aby odpowiedzieć na ostatnią część pytania, ponieważ naprawdę wskazuje to na różnicę między
list
aivector
R:Lista może zawierać dowolną inną klasę jako każdy element. Możesz więc mieć listę, w której pierwszy element jest wektorem znaków, drugi ramką danych itp. W tym przypadku utworzono dwie różne listy.
x
ma cztery wektory, każdy o długości 1.x2
ma 1 wektor o długości 4:Są to więc zupełnie inne listy.
Listy R są bardzo podobne do struktury danych mapy skrótów, ponieważ każdą wartość indeksu można powiązać z dowolnym obiektem. Oto prosty przykład listy zawierającej 3 różne klasy (w tym funkcję):
Biorąc pod uwagę, że ostatnim elementem jest funkcja wyszukiwania, mogę to tak nazwać:
Jako ostatni komentarz na ten temat: należy zauważyć, że a
data.frame
to tak naprawdę lista (zdata.frame
dokumentacji):Właśnie dlatego kolumny w
data.frame
mogą mieć różne typy danych, podczas gdy kolumny w macierzy nie. Jako przykład tutaj próbuję utworzyć macierz z liczbami i znakami:Zwróć uwagę, że nie mogę zmienić typu danych w pierwszej kolumnie na numeryczny, ponieważ druga kolumna ma znaki:
źródło
switch
w R jest przydatna funkcja, której można użyć do tego celu (patrzhelp(switch)
).Jeśli chodzi o twoje pytania, pozwól mi odpowiedzieć na nie w porządku i podać kilka przykładów:
1 ) Lista jest zwracana, jeśli i kiedy instrukcja return ją doda. Rozważać
2 ) Nazwy po prostu nie są ustawione:
3 ) Nie zwracają tego samego. Twój przykład daje
gdzie
x[1]
zwraca pierwszy elementx
- który jest taki sam jakx
. Każdy skalar jest wektorem o długości jeden. Z drugiej stronyx[[1]]
zwraca pierwszy element listy.4 ) Wreszcie, oba są różne, ponieważ tworzą odpowiednio listę zawierającą cztery skalary i listę z pojedynczym elementem (którym jest wektor czterech elementów).
źródło
Value
. Jak w?strsplit
: „Lista o tej samej długości co x”. Należy jednak wziąć pod uwagę, że funkcja może zwracać różne wartości zależne od argumentów (np. Sapply może zwrócić listę lub wektor).Wystarczy wziąć podzbiór swoich pytań:
W tym artykule na temat indeksowania poruszono kwestię różnicy między
[]
i[[]]
.W skrócie [[]] wybiera pojedynczy element z listy i
[]
zwraca listę wybranych elementów. W twoim przykładziex = list(1, 2, 3, 4)'
pozycja 1 jest pojedynczą liczbą całkowitą, alex[[1]]
zwraca pojedynczą 1 ix[1]
zwraca listę z tylko jedną wartością.źródło
A = array( 11:16, c(2,3) ); A[5]
czy 15 jest w płaskiej tablicy?!Jednym z powodów, dla których listy działają tak, jak działają (uporządkowane), jest zaspokojenie zapotrzebowania na zamówiony kontener, który może zawierać dowolny typ w dowolnym węźle, czego nie robią wektory. Listy są ponownie używane do różnych celów w R, w tym do tworzenia podstawy a
data.frame
, która jest listą wektorów dowolnego typu (ale o tej samej długości).Dlaczego te dwa wyrażenia nie zwracają tego samego wyniku?
Aby dodać do odpowiedzi @ Shane, jeśli chcesz uzyskać ten sam wynik, spróbuj:
Który zmusza wektor
1:4
do listy.źródło
Dodajmy jeszcze jeden punkt:
R ma strukturę danych odpowiada dict Pythona w
hash
opakowaniu . Możesz przeczytać o tym w tym poście na blogu z Open Data Group . Oto prosty przykład:Pod względem użyteczności
hash
klasa jest bardzo podobna do listy. Ale wydajność jest lepsza w przypadku dużych zestawów danych.źródło
Mówisz:
I myślę, że sugerujesz, że to jest problem (?). Jestem tutaj, aby ci powiedzieć, dlaczego to nie problem :-). Twój przykład jest nieco prosty, ponieważ kiedy dokonujesz podziału łańcucha, masz listę z elementami o długości 1 elementu, więc wiesz, że
x[[1]]
jest taki sam jakunlist(x)[1]
. Ale co jeśli wynikstrsplit
zwracanych wyników o różnej długości w każdym pojemniku. Zwrócenie wektora (w porównaniu z listą) w ogóle nie wystarczy.Na przykład:
W pierwszym przypadku (
x
: który zwraca listę) możesz powiedzieć, jaka była 2. „część” trzeciego ciągu, npx[[3]][2]
. : Jak możesz zrobić to samo, skoroxx
wyniki zostały „odkryte” (unlist
-ed)?źródło
nie jest taki sam, ponieważ 1: 4 jest taki sam jak c (1,2,3,4). Jeśli chcesz, aby były takie same:
źródło
To bardzo stare pytanie, ale myślę, że nowa odpowiedź może wnieść wartość dodaną, ponieważ moim zdaniem nikt nie zajął się bezpośrednio niektórymi obawami w PO.
Pomimo sugerowanych odpowiedzi,
list
obiekty w R nie są mapami skrótów. Jeśli chcesz zrobić równolegle z pythonem,list
bardziej przypominasz, zgadnij, pythonlist
(tuple
a właściwie s).Lepiej jest opisać, w jaki sposób większość obiektów R jest przechowywanych wewnętrznie (typ C obiektu R to
SEXP
). Składają się zasadniczo z trzech części:NULL
jeśli obiekt nie ma atrybutów).Z wewnętrznego punktu widzenia różnica między a
list
inumeric
wektorem jest na przykład niewielka . Wartości, które przechowują, są po prostu różne. Podzielmy dwa obiekty na opisany wcześniej paradygmat:Dla
x
:numeric
(REALSXP
po stronie C), długość to 10 i inne rzeczy.double
wartości.NULL
, ponieważ obiekt nie ma żadnych.Dla
y
:list
(VECSXP
po stronie C), długość to 2 i inne rzeczy.runif(10)
irunif(3)
.NULL
jak dlax
.Tak więc jedyną różnicą między
numeric
wektorem a alist
jest to, żenumeric
część danych składa się zdouble
wartości, podczas gdy dlalist
części danych jest tablica wskaźników do innych obiektów R.Co dzieje się z imionami? Cóż, nazwy to tylko niektóre atrybuty, które można przypisać do obiektu. Zobaczmy obiekt poniżej:
list
(VECSXP
po stronie C), długość to 2 i inne rzeczy.1:3
iLETTERS
.names
składnikiem, który jest obiektemcharacter
R o wartościc("a","b")
.Z poziomu R można odzyskać atrybuty obiektu za pomocą
attributes
funkcji.Kluczowa wartość typowa dla mapy skrótu w R jest tylko iluzją. Kiedy powiesz:
Oto co się dzieje:
[[
funkcja podzbiór nazywa;"a"
) jest typucharacter
, więc metoda ma poszukać takiej wartości znames
atrybutu (jeśli występuje) obiektuz
;names
atrybutu nie ma,NULL
jest zwracany;"a"
wartość jest w nim przeszukiwana. Jeśli"a"
nie jest nazwą obiektu,NULL
jest zwracany;z[[1]]
.Wyszukiwanie klucz-wartość jest raczej pośrednie i zawsze ma charakter pozycyjny. Warto również pamiętać:
names
in R muszą być łańcuchami (character
wektorami);w mapach skrótów nie można mieć dwóch identycznych kluczy. W R możesz przypisać
names
obiektowi z powtarzanymi wartościami. Na przykład:jest całkowicie poprawny w R. Gdy spróbujesz,
y[["same"]]
pierwsza wartość jest pobierana. Powinieneś wiedzieć, dlaczego w tym momencie.Podsumowując, możliwość nadania obiektowi dowolnych atrybutów daje wygląd czegoś innego niż zewnętrzny punkt widzenia. Ale R
list
nie są w żaden sposób mapami skrótu.źródło
Odnośnie wektorów i koncepcji skrótu / tablicy z innych języków:
Wektory to atomy R. Np.
rpois(1e4,5)
(5 liczb losowych),numeric(55)
(wektor zerowy o długości 55 nad podwójnymi) icharacter(12)
(12 pustych łańcuchów) wszystkie są „podstawowe”.Listy lub wektory mogą mieć
names
.Wektory wymagają, aby wszystko było tego samego typu danych. Patrz na to:
Listy mogą zawierać różne typy danych, co widać w innych odpowiedziach i samym pytaniu PO.
Widziałem języki (ruby, javascript), w których „tablice” mogą zawierać zmienne typy danych, ale na przykład w C ++ „tablice” muszą być tego samego typu. Wierzę, że jest to kwestia prędkości / wydajności: jeśli masz
numeric(1e6)
, znasz jego rozmiar i lokalizację każdego elementu a priori ; jeśli rzecz może zawierać"Flying Purple People Eaters"
jakiś nieznany kawałek, wtedy trzeba przeanalizować różne rzeczy, aby poznać podstawowe fakty.Niektóre standardowe operacje R mają również większy sens, gdy typ jest gwarantowany. Na przykład
cumsum(1:9)
ma sens, acumsum(list(1,2,3,4,5,'a',6,7,8,9))
nie ma, bez zagwarantowania, że typ będzie podwójny.Co do twojego drugiego pytania:
Funkcje zwracają różne typy danych niż cały czas są wprowadzane.
plot
zwraca wykres, nawet jeśli nie pobiera wykresu jako danych wejściowych.Arg
zwraca a,numeric
mimo że zaakceptowałcomplex
. Itp.(A jeśli chodzi o
strsplit
: kod źródłowy jest tutaj .)źródło
Chociaż to dość stare pytanie, muszę powiedzieć, że dotyka dokładnie wiedzy, której brakowało mi podczas pierwszych kroków w R - tj. Jak wyrażać dane w mojej dłoni jako obiekt w R lub jak wybierać z istniejących obiektów. Nowicjuszowi R nie jest łatwo myśleć „w pudełku R.” od samego początku.
Więc sam zacząłem używać kul, które pomogły mi bardzo dużo dowiedzieć się, jakiego obiektu użyć do jakich danych, i po prostu wyobrazić sobie użycie w rzeczywistym świecie.
Chociaż nie udzielam dokładnych odpowiedzi na pytanie, krótki tekst poniżej może pomóc czytelnikowi, który właśnie zaczął od R i zadaje podobne pytania.
[
podzbiory.[
podzbiorów.[
podzbiory według rzędów i kolumn lub według sekwencji.list
gdzie mogę podzbiór za[
pomocą wierszy i kolumn, ale nawet za pomocą[[
.tree structure
którym[i]
wybiera i zwraca całe gałęzie oraz[[i]]
zwraca element z gałęzi. A ponieważ tak jesttree like structure
, możesz nawet użyćindex sequence
do rozwiązania każdego pojedynczego liścia na bardzo złożonymlist
za pomocą jego[[index_vector]]
. Listy mogą być proste lub bardzo złożone i mogą łączyć różne typy obiektów w jeden.Więc
lists
możesz skończyć z większą liczbą sposobów wyboru wleaf
zależności od sytuacji, jak w poniższym przykładzie.Ten sposób myślenia bardzo mi pomógł.
źródło
Jeśli to pomaga, zwykle wyobrażam sobie „listy” w R jako „rekordy” w innych językach sprzed OO:
Nazwa „rekord” koliduje ze standardowym znaczeniem „rekordy” (aka wiersze) w języku bazy danych i być może dlatego ich nazwa sugeruje się: jako listy (pól).
źródło
Dlaczego te dwa różne podmioty,
[ ]
oraz[[ ]]
, zwraca ten sam wynik?[ ]
zapewnia operację ustawienia podrzędnego. Zasadniczo podzestaw dowolnego obiektu będzie miał ten sam typ, co obiekt oryginalny. Dlategox[1]
zapewnia listę. Podobniex[1:2]
jest podzbiorem oryginalnej listy, dlatego jest listą. Dawny.[[ ]]
służy do wyodrębnienia elementu z listy.x[[1]]
jest poprawny i wypakuj pierwszy element z listy.x[[1:2]]
nie jest poprawny, ponieważ[[ ]]
nie zapewnia ustawienia podrzędnego, takiego jak[ ]
.źródło