Breve histórico: Muitas (mais?) Linguagens de programação contemporâneas de uso amplo têm pelo menos um punhado de ADTs [tipos de dados abstratos] em comum, em particular,
string (uma sequência composta de caracteres)
lista (uma coleção ordenada de valores) e
tipo baseado em mapa (uma matriz não ordenada que mapeia chaves para valores)
Na linguagem de programação R, os dois primeiros são implementados como character
e vector
, respectivamente.
Quando comecei a aprender R, duas coisas eram óbvias quase desde o início: list
é o tipo de dados mais importante em R (porque é a classe pai para o R data.frame
) e, segundo, eu simplesmente não conseguia entender como eles funcionavam, pelo menos não é bom o suficiente para usá-los corretamente no meu código.
Por um lado, pareceu-me que o list
tipo de dados de R era uma implementação direta do mapa ADT ( dictionary
em Python, NSMutableDictionary
em Objective C, hash
em Perl e Ruby, object literal
em Javascript e assim por diante).
Por exemplo, você os cria da mesma forma que faria com um dicionário Python, passando pares de valores-chave para um construtor (o que em Python dict
não é list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
E você acessa os itens de uma lista R exatamente como faria em um dicionário Python, por exemplo x['ev1']
,. Da mesma forma, você pode recuperar apenas as 'chaves' ou apenas os 'valores' :
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
mas Rs list
também são diferentes de outros ADTs do tipo mapa (dentre os idiomas que aprendi de qualquer maneira). Meu palpite é que isso é uma consequência da especificação inicial para S, ou seja, uma intenção de projetar uma DSL de dados / estatísticas [idioma específico do domínio] a partir do zero.
três diferenças significativas entre Rs list
e tipos de mapeamento em outros idiomas em uso generalizado (por exemplo, Python, Perl, JavaScript):
primeiro , list
s em R são uma coleção ordenada , assim como vetores, mesmo que os valores sejam digitados (ou seja, as chaves podem ser qualquer valor que possa ser lavável e não apenas números inteiros seqüenciais). Quase sempre, o tipo de dados de mapeamento em outros idiomas não é ordenado .
segundo , list
s podem ser retornados de funções, mesmo que você nunca tenha passado um list
quando você chamou a função, e mesmo que a função que retornou list
não contenha um list
construtor ( explícito) (é claro, você pode lidar com isso na prática, agrupando o resultado retornado em uma chamada para 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
Uma terceira característica peculiar de R's list
: não parece que eles possam ser membros de outro ADT e, se você tentar fazer isso, o contêiner primário será coagido a a list
. Por exemplo,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
minha intenção aqui não é criticar a linguagem ou como ela é documentada; Da mesma forma, não estou sugerindo que haja algo errado com a list
estrutura de dados ou como ela se comporta. Tudo o que eu preciso é corrigir o meu entendimento de como eles funcionam, para que eu possa usá-los corretamente no meu código.
Aqui estão os tipos de coisas que eu gostaria de entender melhor:
Quais são as regras que determinam quando uma chamada de função retornará uma
list
(por exemplo,strsplit
expressão recitada acima)?Se eu não atribuir explicitamente nomes a
list
(por exemplo,list(10,20,30,40)
) , os nomes padrão são apenas números inteiros sequenciais começando com 1? (Presumo, mas estou longe de ter certeza de que a resposta é sim, caso contrário não poderíamos coagir esse tipo delist
vetor a uma chamada paraunlist
.)Por que esses dois operadores diferentes,,
[]
e[[]]
retornam o mesmo resultado?x = list(1, 2, 3, 4)
ambas as expressões retornam "1":
x[1]
x[[1]]
por que essas duas expressões não retornam o mesmo resultado?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Por favor, não me aponte para a Documentação R ( ?list
, R-intro
) - Li-a com atenção e não me ajuda a responder ao tipo de perguntas que recitei logo acima.
(por último, eu aprendi recentemente e comecei a usar um pacote R (disponível no CRAN) chamado hash
que implementa o comportamento do tipo de mapa convencional por meio de uma classe S4; certamente posso recomendar este pacote.)
x = list(1, 2, 3, 4)
, ambos NÃO retornam o mesmo resultado:,x[1]
ex[[1]]
. O primeiro retorna uma lista e o segundo retorna um vetor numérico. Rolando abaixo, parece-me que Dirk foi o único entrevistado a responder corretamente a essa pergunta.list
em R não é como um hash. Tenho mais uma que acho digna de nota.list
em R pode ter dois membros com o mesmo nome de referência. Considere queobj <- c(list(a=1),list(a=2))
é válido e retorna uma lista com dois valores nomeados de 'a'. Nesse caso, uma chamadaobj["a"]
retornará apenas o primeiro elemento da lista correspondente. Você pode obter um comportamento semelhante (talvez idêntico) a um hash com apenas um item por nomes referenciados usando ambientes em R. por exemplox <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
Respostas:
Apenas para abordar a última parte da sua pergunta, já que isso realmente indica a diferença entre a
list
evector
em R:Uma lista pode conter qualquer outra classe como cada elemento. Assim, você pode ter uma lista em que o primeiro elemento é um vetor de caractere, o segundo é um quadro de dados, etc. Nesse caso, você criou duas listas diferentes.
x
possui quatro vetores, cada um com comprimento 1.x2
possui 1 vetor com comprimento 4:Então, essas são listas completamente diferentes.
As listas R são muito parecidas com uma estrutura de dados de mapa de hash , em que cada valor de índice pode ser associado a qualquer objeto. Aqui está um exemplo simples de uma lista que contém 3 classes diferentes (incluindo uma função):
Dado que o último elemento é a função de pesquisa, posso chamar assim:
Como um comentário final sobre isso: deve-se notar que a
data.frame
é realmente uma lista (dadata.frame
documentação):É por isso que as colunas em uma
data.frame
podem ter tipos de dados diferentes, enquanto as colunas em uma matriz não podem. Como exemplo, aqui eu tento criar uma matriz com números e caracteres:Observe como não posso alterar o tipo de dados na primeira coluna para numérico porque a segunda coluna possui caracteres:
fonte
switch
função útil em R que possa ser usada para esse fim (verhelp(switch)
).Em relação às suas perguntas, deixe-me resolvê-las em ordem e dar alguns exemplos:
1 ) Uma lista será retornada se e quando a instrução de retorno adicionar uma. Considerar
2 ) Os nomes simplesmente não estão definidos:
3 ) Eles não retornam a mesma coisa. Seu exemplo dá
onde
x[1]
retorna o primeiro elemento dex
- que é o mesmo quex
. Todo escalar é um vetor de comprimento um. Por outro lado,x[[1]]
retorna o primeiro elemento da lista.4 ) Por fim, os dois são diferentes entre eles, criando, respectivamente, uma lista contendo quatro escalares e uma lista com um único elemento (que por acaso é um vetor de quatro elementos).
fonte
Value
. Como em?strsplit
: "Uma lista do mesmo tamanho que x". Mas você deve considerar que pode haver uma função retornar valores diferentes, dependendo dos argumentos (por exemplo, sapply pode retornar lista ou vetor).Apenas para fazer um subconjunto de suas perguntas:
Este artigo sobre indexação aborda a questão da diferença entre
[]
e[[]]
.Em resumo [[]] seleciona um único item de uma lista e
[]
retorna uma lista dos itens selecionados. No seu exemplo, ox = list(1, 2, 3, 4)'
item 1 é um único inteiro, masx[[1]]
retorna um único 1 ex[1]
retorna uma lista com apenas um valor.fonte
A = array( 11:16, c(2,3) ); A[5]
é 15, na matriz plana ?!Um dos motivos pelos quais as listas funcionam (ordenadas) é atender à necessidade de um contêiner ordenado que possa conter qualquer tipo em qualquer nó, que vetores não possuem. As listas são reutilizadas para diversos propósitos em R, incluindo a formação da base de a
data.frame
, que é uma lista de vetores do tipo arbitrário (mas com o mesmo comprimento).Por que essas duas expressões não retornam o mesmo resultado?
Para adicionar à resposta de @ Shane, se você deseja obter o mesmo resultado, tente:
O que força o vetor
1:4
em uma lista.fonte
Apenas para adicionar mais um ponto a isso:
R tem uma estrutura de dados equivalente à Dict Python no
hash
pacote . Você pode ler sobre isso nesta postagem do blog do Open Data Group . Aqui está um exemplo simples:Em termos de usabilidade, a
hash
classe é muito semelhante a uma lista. Mas o desempenho é melhor para grandes conjuntos de dados.fonte
Você diz:
E eu acho que você sugere que isso é um problema (?). Estou aqui para lhe dizer por que não é um problema :-). Seu exemplo é um pouco simples, pois quando você faz a divisão de cadeias, você tem uma lista com elementos com 1 elemento, então você sabe que
x[[1]]
é o mesmo queunlist(x)[1]
. Mas e se o resultado dosstrsplit
resultados retornados de comprimento diferente em cada posição. Simplesmente retornar um vetor (versus uma lista) não funciona.Por exemplo:
No primeiro caso (
x
: que retorna uma lista), você pode dizer que a "parte" 2nd da 3ª corda foi, por exemplo:x[[3]][2]
. Como você pode fazer o mesmo usandoxx
agora que os resultados foram "desvendados" (unlist
-ed)?fonte
não é o mesmo porque 1: 4 é o mesmo que c (1,2,3,4). Se você deseja que eles sejam iguais, então:
fonte
Essa é uma pergunta muito antiga, mas acho que uma nova resposta pode agregar algum valor, pois, na minha opinião, ninguém abordou diretamente algumas das preocupações do OP.
Apesar do que as respostas aceitas sugerem, os
list
objetos em R não são mapas de hash. Se você deseja fazer um paralelo com o python,list
é mais parecido com o pythonlist
s (outuple
na verdade).É melhor descrever como a maioria dos objetos R é armazenada internamente (o tipo C de um objeto R é
SEXP
). Eles são feitos basicamente de três partes:NULL
se o objeto não tiver atributos).Do ponto de vista interno, há pouca diferença entre a
list
e umnumeric
vetor, por exemplo. Os valores que eles armazenam são apenas diferentes. Vamos quebrar dois objetos no paradigma descrito anteriormente:Para
x
:numeric
(REALSXP
no lado C), o comprimento é 10 e outras coisas.double
valores.NULL
, pois o objeto não possui nenhum.Para
y
:list
(VECSXP
no lado C), o comprimento é 2 e outras coisas.runif(10)
erunif(3)
respectivamente.NULL
, como parax
.Portanto, a única diferença entre um
numeric
vetor e alist
é que anumeric
parte de dados é composta dedouble
valores, enquanto que alist
parte de dados é uma matriz de ponteiros para outros objetos R.O que acontece com os nomes? Bem, nomes são apenas alguns dos atributos que você pode atribuir a um objeto. Vamos ver o objeto abaixo:
list
(VECSXP
no lado C), o comprimento é 2 e outras coisas.1:3
eLETTERS
respectivamente.names
componente que é umcharacter
objeto R com valorc("a","b")
.No nível R, você pode recuperar os atributos de um objeto com a
attributes
funçãoO valor-chave típico de um mapa de hash em R é apenas uma ilusão. Quando voce diz:
Isto é o que acontece:
[[
função de subconjunto é chamada;"a"
) é do tipocharacter
; portanto, o método é instruído a pesquisar esse valor nonames
atributo (se presente) do objetoz
;names
atributo não estiver lá,NULL
será retornado;"a"
valor é pesquisado nele. Se"a"
não for um nome do objeto,NULL
será retornado;z[[1]]
.A pesquisa de valor-chave é bastante indireta e é sempre posicional. Além disso, é útil ter em mente:
names
em R devem ser cadeias (character
vetores);nos mapas de hash, você não pode ter duas chaves idênticas. Em R, você pode atribuir
names
a um objeto com valores repetidos. Por exemplo:é perfeitamente válido em R. Quando você tenta,
y[["same"]]
o primeiro valor é recuperado. Você deve saber o porquê neste momento.Em conclusão, a capacidade de atribuir atributos arbitrários a um objeto fornece a aparência de algo diferente de um ponto de vista externo. Mas Rs
list
não são mapas de hash de forma alguma.fonte
Em relação aos vetores e ao conceito de hash / array de outros idiomas:
Os vetores são os átomos de R. Por exemplo,
rpois(1e4,5)
(5 números aleatórios),numeric(55)
(vetor zero com mais de 55 vezes o dobro) echaracter(12)
(12 cadeias vazias), são todos "básicos".Listas ou vetores podem ter
names
.Os vetores exigem que tudo seja do mesmo tipo de dados. Vê isto:
As listas podem conter tipos de dados variados, como visto em outras respostas e na própria pergunta do OP.
Vi linguagens (ruby, javascript) nas quais "matrizes" podem conter tipos de dados variáveis, mas, por exemplo, em C ++, "matrizes" devem ser todos do mesmo tipo de dados. Eu acredito que isso é uma questão de velocidade / eficiência: se você tem um,
numeric(1e6)
sabe o tamanho e a localização de cada elemento a priori ; se a coisa puder conter"Flying Purple People Eaters"
alguma fatia desconhecida, você precisará analisar as coisas para conhecer fatos básicos sobre ela.Certas operações R padrão também fazem mais sentido quando o tipo é garantido. Por exemplo,
cumsum(1:9)
faz sentido, ao passocumsum(list(1,2,3,4,5,'a',6,7,8,9))
que não, sem que o tipo seja garantido como duplo.Quanto à sua segunda pergunta:
As funções retornam tipos de dados diferentes dos que são inseridos o tempo todo.
plot
retorna uma plotagem, mesmo que ela não seja plotada como entrada.Arg
retorna anumeric
mesmo que tenha aceito acomplex
. Etc.(E quanto a
strsplit
: o código fonte está aqui .)fonte
Embora essa seja uma pergunta bastante antiga, devo dizer que está tocando exatamente o conhecimento que estava perdendo durante meus primeiros passos no R - ou seja, como expressar dados na minha mão como objeto no R ou como selecionar objetos existentes. Não é fácil para um novato em R pensar "em uma caixa em R" desde o início.
Então eu mesmo comecei a usar muletas abaixo, o que me ajudou muito a descobrir qual objeto usar para quais dados e basicamente a imaginar o uso no mundo real.
Embora eu não esteja dando respostas exatas para a pergunta, o pequeno texto abaixo pode ajudar o leitor que acabou de começar com R e está fazendo perguntas semelhantes.
[
subconjuntos.[
subconjuntos.[
subconjuntos por linhas e colunas ou por sequência.list
onde posso subconjunto usando[
linhas e colunas, mas mesmo usando[[
.tree structure
onde[i]
seleciona e retorna ramificações inteiras e[[i]]
retorna itens da ramificação. E, como étree like structure
, você pode até usar umindex sequence
para endereçar cada folha de uma forma muito complexalist
usando sua[[index_vector]]
. As listas podem ser simples ou muito complexas e podem misturar vários tipos de objetos em um.Assim,
lists
você pode descobrir mais maneiras de selecionar umaleaf
dependendo da situação, como no exemplo a seguir.Essa maneira de pensar me ajudou muito.
fonte
Se ajudar, eu tendem a conceber "listas" em R como "registros" em outros idiomas pré-OO:
O nome "registro" entraria em conflito com o significado padrão de "registros" (também conhecido como linhas) na linguagem do banco de dados, e pode ser por isso que o nome deles se sugeriu: como listas (de campos).
fonte
por que esses dois operadores diferentes
[ ]
, e[[ ]]
retornam o mesmo resultado?[ ]
fornece operação de subconfiguração. Em geral, o subconjunto de qualquer objeto terá o mesmo tipo que o objeto original. Portanto,x[1]
fornece uma lista. Da mesma forma,x[1:2]
é um subconjunto da lista original, portanto, é uma lista. Ex.[[ ]]
é para extrair um elemento da lista.x[[1]]
é válido e extrai o primeiro elemento da lista.x[[1:2]]
não é válido, pois[[ ]]
não fornece subconjuntos como[ ]
.fonte