Kurzer Hintergrund: Viele (die meisten?) Zeitgenössische Programmiersprachen, die weit verbreitet sind, haben mindestens eine Handvoll ADTs (abstrakte Datentypen) gemeinsam, insbesondere
Zeichenfolge (eine Folge von Zeichen)
Liste (eine geordnete Sammlung von Werten) und
Kartenbasierter Typ (ein ungeordnetes Array, das Schlüssel Werten zuordnet)
In der Programmiersprache R sind die ersten beiden als character
und implementiertvector
.
Als ich anfing, R zu lernen, waren fast von Anfang an zwei Dinge offensichtlich: list
ist der wichtigste Datentyp in R (weil es die übergeordnete Klasse für das R istdata.frame
), und zweitens konnte ich zumindest nicht verstehen, wie sie funktionierten nicht gut genug, um sie in meinem Code richtig zu verwenden.
Zum einen schien mir der list
Datentyp von R eine einfache Implementierung der Map ADT zu sein ( dictionary
in Python, NSMutableDictionary
in Objective C, hash
in Perl und Ruby,object literal
in Javascript usw.).
Sie erstellen sie beispielsweise wie ein Python-Wörterbuch, indem Sie Schlüssel-Wert-Paare an einen Konstruktor übergeben (was in Python dict
nicht der Fall ist list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
Und Sie greifen auf die Elemente einer R-Liste zu, genau wie auf die eines Python-Wörterbuchs, z x['ev1']
. Ebenso können Sie nur die 'Schlüssel' oder nur die 'Werte' abrufen , indem Sie:
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
Aber R list
s sind auch anders als andere ADTs vom Kartentyp (unter den Sprachen, die ich sowieso gelernt habe). Ich vermute, dass dies eine Folge der ursprünglichen Spezifikation für S ist, dh die Absicht, eine Daten- / Statistik-DSL [domänenspezifische Sprache] von Grund auf zu entwerfen.
Drei signifikante Unterschiede zwischen list
Rs und Mapping-Typen in anderen weit verbreiteten Sprachen (z. B. Python, Perl, JavaScript):
Erstens sind list
s in R eine geordnete Sammlung, genau wie Vektoren, obwohl die Werte verschlüsselt sind (dh die Schlüssel können beliebige Hash-Werte sein, nicht nur sequentielle Ganzzahlen). Fast immer ist der Mapping-Datentyp in anderen Sprachen ungeordnet .
zweite , list
kann s von Funktionen zurückgegeben werden , obwohl Sie nie in einem übergeben , list
wenn Sie die Funktion aufgerufen, und auch wenn die Funktion , die zurückgegeben die list
keinen (expliziten) enthalten list
Konstruktor (Natürlich können Sie mit diesem in der Praxis beschäftigen , indem Umschließen des zurückgegebenen Ergebnisses in einen Aufruf von 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
Eine dritte Besonderheit von list
Rs: Es scheint nicht, dass sie Mitglieder eines anderen ADT sein können, und wenn Sie dies versuchen, wird der primäre Container zu a gezwungen list
. Z.B,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
Ich habe hier nicht die Absicht, die Sprache oder deren Dokumentation zu kritisieren. Ebenso schlage ich nicht vor, dass irgendetwas mit dem nicht stimmtlist
Datenstruktur oder ihrem Verhalten . Ich muss nur korrigieren, wie sie funktionieren, damit ich sie in meinem Code richtig verwenden kann.
Hier sind die Dinge, die ich besser verstehen möchte:
Welche Regeln bestimmen, wann ein Funktionsaufruf ein
list
(z. B. denstrsplit
oben angegebenen Ausdruck) zurückgibt?Wenn ich a
list
(z. B.list(10,20,30,40)
) nicht explizit Namen zuweise, sind die Standardnamen nur sequentielle Ganzzahlen, die mit 1 beginnen? (Ich nehme an, aber ich bin weit davon entfernt, sicher zu sein, dass die Antwort ja lautet, sonst könnten wir diese Art von nichtlist
zu einem Vektor mit einem Aufruf von zwingenunlist
.)Warum geben diese beiden unterschiedlichen Operatoren
[]
und[[]]
das gleiche Ergebnis zurück?x = list(1, 2, 3, 4)
beide Ausdrücke geben "1" zurück:
x[1]
x[[1]]
Warum geben diese beiden Ausdrücke nicht dasselbe Ergebnis zurück?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Bitte verweisen Sie mich nicht auf die R-Dokumentation ( ?list
, R-intro
) - ich habe sie sorgfältig gelesen und sie hilft mir nicht bei der Beantwortung der oben genannten Fragen.
(Zuletzt habe ich kürzlich von einem R-Paket (verfügbar auf CRAN) erfahren, hash
das herkömmliches Verhalten vom Kartentyp über eine S4-Klasse implementiert . Ich kann dieses Paket auf jeden Fall empfehlen.)
x = list(1, 2, 3, 4)
beide NICHT das gleiche Ergebnis zurück:,x[1]
undx[[1]]
. Der erste gibt eine Liste zurück und der zweite gibt einen numerischen Vektor zurück. Wenn ich unten scrolle, scheint mir Dirk der einzige Befragte zu sein, der diese Frage richtig beantwortet hat.list
in R nicht wie ein Hash sind. Ich habe noch eine, die ich für bemerkenswert halte.list
in R können zwei Mitglieder mit demselben Referenznamen sein. Betrachten Sie diesobj <- c(list(a=1),list(a=2))
als gültig und geben Sie eine Liste mit zwei benannten Werten von 'a' zurück. In diesem Fall gibt ein Aufruf vonobj["a"]
nur das erste übereinstimmende Listenelement zurück. Sie können ein ähnliches (möglicherweise identisches) Verhalten wie ein Hash mit nur einem Element pro referenziertem Namen in Umgebungen in Rx <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
Antworten:
Nur um den letzten Teil Ihrer Frage anzusprechen, da dies wirklich den Unterschied zwischen a
list
undvector
in R aufzeigt:Eine Liste kann als jedes Element eine andere Klasse enthalten. Sie können also eine Liste erstellen, in der das erste Element ein Zeichenvektor, das zweite ein Datenrahmen usw. ist. In diesem Fall haben Sie zwei verschiedene Listen erstellt.
x
hat vier Vektoren mit jeweils der Länge 1.x2
hat 1 Vektor der Länge 4:Das sind also völlig andere Listen.
R-Listen ähneln stark einer Hash-Map- Datenstruktur, da jeder Indexwert jedem Objekt zugeordnet werden kann. Hier ist ein einfaches Beispiel für eine Liste, die 3 verschiedene Klassen enthält (einschließlich einer Funktion):
Da das letzte Element die Suchfunktion ist, kann ich es so nennen:
Als abschließender Kommentar dazu: Es sollte beachtet werden, dass a
data.frame
wirklich eine Liste ist (aus derdata.frame
Dokumentation):Aus diesem Grund können Spalten in a
data.frame
unterschiedliche Datentypen haben, Spalten in einer Matrix jedoch nicht. Als Beispiel versuche ich hier eine Matrix mit Zahlen und Zeichen zu erstellen:Beachten Sie, dass ich den Datentyp in der ersten Spalte nicht in numerisch ändern kann, da die zweite Spalte Zeichen enthält:
quelle
switch
in R eine nützliche Funktion gibt, die für diesen Zweck verwendet werden kann (siehehelp(switch)
).Lassen Sie mich Ihre Fragen der Reihe nach beantworten und einige Beispiele nennen:
1 ) Eine Liste wird zurückgegeben, wenn die return-Anweisung eine hinzufügt. Erwägen
2 ) Namen werden einfach nicht gesetzt:
3 ) Sie geben nicht dasselbe zurück. Ihr Beispiel gibt
Dabei wird
x[1]
das erste Element vonx
- zurückgegeben, das dasselbe ist wiex
. Jeder Skalar ist ein Vektor der Länge eins. Auf der anderen Seite wirdx[[1]]
das erste Element der Liste zurückgegeben.4 ) Schließlich unterscheiden sich die beiden zwischen der Erstellung einer Liste mit vier Skalaren und einer Liste mit einem einzelnen Element (das zufällig ein Vektor aus vier Elementen ist).
quelle
Value
. Wie in?strsplit
: "Eine Liste mit der gleichen Länge wie x". Sie sollten jedoch berücksichtigen, dass es eine Funktion geben kann, die abhängig von den Argumenten unterschiedliche Werte zurückgibt (z. B. sapply kann Liste oder Vektor zurückgeben).Nur um einen Teil Ihrer Fragen zu beantworten:
Dieser Artikel zur Indizierung befasst sich mit der Frage nach dem Unterschied zwischen
[]
und[[]]
.Kurz gesagt, [[]] wählt ein einzelnes Element aus einer Liste aus und
[]
gibt eine Liste der ausgewählten Elemente zurück. In Ihrem Beispiel istx = list(1, 2, 3, 4)'
Element 1 eine einzelne Ganzzahl,x[[1]]
gibt jedoch eine einzelne 1 undx[1]
eine Liste mit nur einem Wert zurück.quelle
A = array( 11:16, c(2,3) ); A[5]
15 in der flachen Anordnung ?!Ein Grund, warum Listen so funktionieren (geordnet), besteht darin, die Notwendigkeit eines geordneten Containers zu adressieren, der an jedem Knoten einen beliebigen Typ enthalten kann, was Vektoren nicht tun. Listen werden in R für eine Vielzahl von Zwecken wiederverwendet, einschließlich der Bildung der Basis von a
data.frame
, einer Liste von Vektoren beliebigen Typs (aber gleicher Länge).Warum geben diese beiden Ausdrücke nicht dasselbe Ergebnis zurück?
Wenn Sie das gleiche Ergebnis erzielen möchten, versuchen Sie Folgendes, um die Antwort von @ Shane zu ergänzen:
Was den Vektor
1:4
in eine Liste zwingt .quelle
Nur um noch einen Punkt hinzuzufügen:
R hat eine Datenstruktur , in den Python zu dict gleichwertig dem
hash
Paket . Sie können darüber in diesem Blog-Beitrag aus der Open Data Group lesen . Hier ist ein einfaches Beispiel:In Bezug auf die Benutzerfreundlichkeit ist die
hash
Klasse einer Liste sehr ähnlich. Bei großen Datenmengen ist die Leistung jedoch besser.quelle
Du sagst:
Und ich denke, Sie schlagen vor, dass dies ein Problem ist (?). Ich bin hier, um dir zu sagen, warum es kein Problem ist :-). Ihr Beispiel ist ein bisschen einfach: Wenn Sie den String-Split durchführen, haben Sie eine Liste mit Elementen, die 1 Element lang sind, sodass Sie wissen, dass dies
x[[1]]
dasselbe ist wieunlist(x)[1]
. Was aber, wenn das Ergebnis derstrsplit
zurückgegebenen Ergebnisse in jedem Bin unterschiedlich lang ist? Das einfache Zurückgeben eines Vektors (im Vergleich zu einer Liste) reicht überhaupt nicht aus.Zum Beispiel:
Im ersten Fall (
x
: der eine Liste zurückgibt) können Sie feststellen, was der 2. "Teil" der 3. Zeichenfolge war, zx[[3]][2]
. Wie können Sie dasselbe tun,xx
wenn die Ergebnisse "entwirrt" (unlist
-ed) sind?quelle
ist nicht dasselbe, weil 1: 4 dasselbe ist wie c (1,2,3,4). Wenn Sie möchten, dass sie gleich sind, dann:
quelle
Dies ist eine sehr alte Frage, aber ich denke, dass eine neue Antwort einen Mehrwert bringen könnte, da meiner Meinung nach niemand einige der Bedenken im OP direkt angesprochen hat.
Ungeachtet dessen, was die akzeptierten Antworten nahelegen, sind
list
Objekte in R keine Hash-Maps. Wenn Sie eine Parallele zu Python machen möchten,list
sind Sie eher wie Pythonlist
s (odertuple
tatsächlich s).Es ist besser zu beschreiben, wie die meisten R-Objekte intern gespeichert werden (der C-Typ eines R-Objekts ist
SEXP
). Sie bestehen im Wesentlichen aus drei Teilen:NULL
wenn das Objekt keine Attribute hat).Aus interner Sicht gibt es zum Beispiel kaum einen Unterschied zwischen a
list
und einemnumeric
Vektor. Die Werte, die sie speichern, sind einfach unterschiedlich. Lassen Sie uns zwei Objekte in das zuvor beschriebene Paradigma einteilen:Für
x
:numeric
(REALSXP
auf der C-Seite) ist, die Länge 10 ist und andere Sachen.double
Werten.NULL
, da das Objekt keine hat.Für
y
:list
(VECSXP
auf der C-Seite) ist, die Länge 2 ist und andere Sachen.runif(10)
undrunif(3)
jeweils.NULL
wie fürx
.Der einzige Unterschied zwischen einem
numeric
Vektor und alist
besteht also darin, dass dernumeric
Datenteil ausdouble
Werten besteht, währendlist
der Datenteil ein Array von Zeigern auf andere R-Objekte ist.Was passiert mit Namen? Namen sind nur einige der Attribute, die Sie einem Objekt zuweisen können. Sehen wir uns das folgende Objekt an:
list
(VECSXP
auf der C-Seite) ist, die Länge 2 ist und andere Sachen.1:3
undLETTERS
jeweils.names
Komponente, die eincharacter
R-Objekt mit Wert istc("a","b")
.Auf der R-Ebene können Sie mit der
attributes
Funktion die Attribute eines Objekts abrufen .Der für eine Hash-Map in R typische Schlüsselwert ist nur eine Illusion. Wenn du sagst:
das ist, was passiert:
[[
Teilmengenfunktion wird aufgerufen;"a"
) ist vom Typcharacter
, daher wird die Methode angewiesen, diesen Wert anhand desnames
Attributs (falls vorhanden) des Objekts zu suchenz
.names
Attribut nicht vorhanden ist,NULL
wird es zurückgegeben."a"
Wert darin gesucht. Wenn"a"
es sich nicht um einen Namen des Objekts handelt,NULL
wird dieser zurückgegeben.z[[1]]
.Die Schlüsselwertsuche ist eher indirekt und immer positionell. Auch nützlich zu beachten:
names
in R müssen Strings (character
Vektoren) sein;In Hash-Maps können Sie nicht zwei identische Schlüssel haben. In R können Sie
names
einem Objekt mit wiederholten Werten zuweisen . Zum Beispiel:ist in R vollkommen gültig. Wenn Sie versuchen, wird
y[["same"]]
der erste Wert abgerufen. Sie sollten an dieser Stelle wissen, warum.Zusammenfassend lässt sich sagen, dass die Möglichkeit, einem Objekt beliebige Attribute zuzuweisen, von außen etwas anderes erscheinen lässt. Aber
list
Rs sind in keiner Weise Hash-Maps.quelle
In Bezug auf Vektoren und das Hash / Array-Konzept aus anderen Sprachen:
Vektoren sind die Atome von R. ZB
rpois(1e4,5)
(5 Zufallszahlen),numeric(55)
(Länge-55-Nullvektor über Doppel) undcharacter(12)
(12 leere Zeichenfolgen) sind alle "grundlegend".Entweder Listen oder Vektoren können haben
names
.Vektoren erfordern, dass alles vom gleichen Datentyp ist. Schau dir das an:
Listen können unterschiedliche Datentypen enthalten, wie aus anderen Antworten und der Frage des OP selbst hervorgeht.
Ich habe Sprachen (Ruby, Javascript) gesehen, in denen "Arrays" variable Datentypen enthalten können, aber in C ++ müssen "Arrays" beispielsweise alle denselben Datentyp haben. Ich glaube, das ist eine Sache mit Geschwindigkeit / Effizienz: Wenn Sie eine haben
numeric(1e6)
, kennen Sie deren Größe und die Position jedes Elements a priori ; Wenn das Ding"Flying Purple People Eaters"
in einem unbekannten Slice enthalten sein könnte , müssen Sie tatsächlich Dinge analysieren, um grundlegende Fakten darüber zu erfahren.Bestimmte Standard-R-Operationen sind auch dann sinnvoller, wenn der Typ garantiert ist. Zum Beispiel
cumsum(1:9)
macht dies Sinn, währendcumsum(list(1,2,3,4,5,'a',6,7,8,9))
dies nicht der Fall ist, ohne dass der Typ garantiert doppelt ist.Zu Ihrer zweiten Frage:
Funktionen geben andere Datentypen zurück, als sie ständig eingegeben werden.
plot
Gibt einen Plot zurück, obwohl kein Plot als Eingabe verwendet wird.Arg
gibt a zurücknumeric
, obwohl es a akzeptiert hatcomplex
. Usw.(Und was
strsplit
: der Quellcode ist hier .)quelle
Obwohl dies eine ziemlich alte Frage ist, muss ich sagen, dass sie genau das Wissen berührt, das mir bei meinen ersten Schritten in R gefehlt hat - dh wie man Daten in meiner Hand als Objekt in R ausdrückt oder wie man aus vorhandenen Objekten auswählt. Für einen R-Neuling ist es nicht einfach, von Anfang an "in einer R-Box" zu denken.
Also habe ich selbst angefangen, Krücken zu verwenden, was mir sehr geholfen hat, herauszufinden, welches Objekt für welche Daten verwendet werden soll, und mir im Grunde eine reale Verwendung vorzustellen.
Obwohl ich keine genauen Antworten auf die Frage gebe, könnte der kurze Text unten dem Leser helfen, der gerade mit R angefangen hat und ähnliche Fragen stellt.
[
Teilmengen.[
Teilmengen.[
Teilmengen nach Zeilen und Spalten oder nach Reihenfolge.list
bei der ich mithilfe[
von Zeilen und Spalten, aber sogar mithilfe von Teilmengen unterteilen kann[[
.tree structure
wo[i]
ganze Zweige ausgewählt und zurückgegeben werden und[[i]]
Artikel aus dem Zweig zurückgegeben werden. Und weil es so isttree like structure
, können Sie sogar ein verwendenindex sequence
, um jedes einzelne Blatt eines sehr komplexenlist
mit seinem zu adressieren[[index_vector]]
. Listen können einfach oder sehr komplex sein und verschiedene Objekttypen zu einem zusammenfassen.So haben
lists
Sie am Ende mehr Möglichkeiten, wie Sieleaf
je nach Situation eine auswählen können, wie im folgenden Beispiel.Diese Denkweise hat mir sehr geholfen.
quelle
Wenn es hilft, neige ich dazu, "Listen" in R als "Datensätze" in anderen Pre-OO-Sprachen zu verstehen:
Der Name "Datensatz" würde mit der Standardbedeutung von "Datensätzen" (auch bekannt als Zeilen) in der Datenbanksprache kollidieren, und möglicherweise hat sich der Name deshalb selbst vorgeschlagen: als Listen (von Feldern).
quelle
Warum geben diese beiden unterschiedlichen Operatoren
[ ]
und[[ ]]
das gleiche Ergebnis zurück?[ ]
bietet Untereinstellungsbetrieb. Im Allgemeinen hat die Teilmenge eines Objekts denselben Typ wie das ursprüngliche Objekt. Daherx[1]
stellt eine Liste. In ähnlicher Weisex[1:2]
handelt es sich um eine Teilmenge der ursprünglichen Liste, daher handelt es sich um eine Liste. Ex.[[ ]]
dient zum Extrahieren eines Elements aus der Liste.x[[1]]
ist gültig und extrahiert das erste Element aus der Liste.x[[1:2]]
ist nicht gültig, da[[ ]]
keine Untereinstellung wie[ ]
.quelle