Existe uma maneira de obter o nome do índice da lista na minha função lapply ()?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
Perguntei antes se é possível preservar os nomes de índice na lista retornada lapply () , mas ainda não sei se existe uma maneira fácil de buscar cada nome de elemento dentro da função personalizada. Gostaria de evitar chamar lapply nos próprios nomes, prefiro obter o nome nos parâmetros da função.
Respostas:
Infelizmente,
lapply
apenas fornece os elementos do vetor que você passa. A solução alternativa usual é passar os nomes ou índices do vetor em vez do próprio vetor.Mas observe que você sempre pode passar argumentos extras para a função, portanto, o seguinte funciona:
Aqui eu uso
lapply
sobre os índices dex
, mas também passox
e os nomes dex
. Como você pode ver, a ordem dos argumentos da função pode ser qualquer coisa -lapply
passará no "elemento" (aqui o índice) para o primeiro argumento não especificado entre os extras. Nesse caso, eu especificoy
en
, então sói
resta ...Que produz o seguinte:
UPDATE Exemplo mais simples, mesmo resultado:
Aqui, a função usa a variável "global"
x
e extrai os nomes em cada chamada.fonte
y
vez de, dex
modo que (espero) seja mais claro que a função possa chamar seus argumentos de qualquer coisa. Também alterou os valores do vetor para11,12,13
.Isso basicamente usa a mesma solução alternativa que Tommy, mas com
Map()
, não há necessidade de acessar variáveis globais que armazenam os nomes dos componentes da lista.Ou, se você preferir
mapply()
fonte
mapply()
, observe aSIMPLIFY
opção, cujo padrão é true. No meu caso, isso transformou tudo em uma matriz grande quando eu só queria aplicar uma lista simples. A configuração paraF
(dentro demapply()
) fez com que fosse executado conforme o planejado.UPDATE para R versão 3.2
Isenção de responsabilidade: este é um truque hacky e pode parar de funcionar nos próximos lançamentos.
Você pode obter o índice usando este:
Nota:
[]
é necessário que isso funcione, pois leva R a pensar que o símboloi
(residente no quadro de avaliação delapply
) pode ter mais referências, ativando assim a duplicação lenta. Sem ele, R não manterá cópias separadas dei
:Outros truques exóticos podem ser usados, como
function(x){parent.frame()$i+0}
oufunction(x){--parent.frame()$i}
.Impacto no desempenho
A duplicação forçada causará perda de desempenho? Sim! aqui estão os benchmarks:
Conclusão
Essa resposta mostra apenas que você NÃO deve usar isso ... Não apenas seu código ficará mais legível se você encontrar outra solução como a de Tommy acima e mais compatível com versões futuras, também corre o risco de perder as otimizações para as quais a equipe principal trabalhou arduamente. desenvolve!
Truques das versões antigas, não funcionam mais:
Resultado:
Explicação:
lapply
cria chamadas do formulárioFUN(X[[1L]], ...)
,FUN(X[[2L]], ...)
etc. Portanto, o argumento que passa éX[[i]]
ondei
está o índice atual no loop. Se obtivermos isso antes de ser avaliado (ou seja, se usarmossubstitute
), obteremos a expressão não avaliadaX[[i]]
. Esta é uma chamada à[[
função, com argumentosX
(um símbolo) ei
(um número inteiro). Então,substitute(x)[[3]]
retorna exatamente esse número inteiro.Com o índice, é possível acessar os nomes trivialmente, se você o salvar primeiro assim:
Resultado:
Ou usando este segundo truque: :-)
(resultado é o mesmo).
Explicação 2:
sys.call(1)
retornalapply(...)
, para quesys.call(1)[[2]]
seja a expressão usada como argumento da listalapply
. Passar isso paraeval
cria um objeto legítimo quenames
pode acessar. Complicado, mas funciona.Bônus: uma segunda maneira de obter os nomes:
Observe que
X
é um objeto válido no quadro pai deFUN
e faz referência ao argumento de lista delapply
, para que possamos chegar a eleeval.parent
.fonte
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
está retornando tudo para ser 3. Você explica como esse 3 foi escolhido? e motivo da discrepância? É igual ao comprimento da lista, neste caso, 3. Desculpe-nos se esta é uma pergunta básica, mas gostaria de saber como aplicá-la em um caso geral.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
trabalho ... vou verificar o que está acontecendo.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
não está mais funcionando e dá um erro,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
existe uma maneira fácil de corrigir isso?Eu já tive o mesmo problema várias vezes ... comecei a usar de outra maneira ... Em vez de usar
lapply
, comecei a usarmapply
fonte
Você pode tentar usar
imap()
dopurrr
pacote.A partir da documentação:
Então, você pode usá-lo dessa maneira:
O que lhe dará o seguinte resultado:
fonte
Apenas faça um loop nos nomes.
fonte
mylist
dentro da função. Melhor ainda a fazerfunction(mylist, nm) ...
A resposta de Tommy se aplica a vetores nomeados, mas tive a ideia de que você estava interessado em listas. E parece que ele estava dando uma reviravolta porque estava fazendo referência a "x" do ambiente de chamada. Essa função usa apenas os parâmetros que foram passados para a função e, portanto, não faz suposições sobre o nome dos objetos que foram passados:
fonte
NULL
?! Entãolapply(x, function(x) NULL)
dá a mesma resposta ...lapply
sempre adiciona os nomes dex
ao resultado posteriormente .Minha resposta vai na mesma direção que Tommy e caracals, mas evita ter que salvar a lista como um objeto adicional.
Resultado:
Isso fornece a lista como um argumento nomeado para FUN (em vez de exibir). lapply só precisa iterar sobre os elementos da lista (tenha cuidado para alterar esse primeiro argumento para lapply ao alterar o comprimento da lista).
Nota: Fornecer a lista diretamente para dobrar como argumento adicional também funciona:
fonte
Tanto o @caracals quanto o @Tommy são boas soluções e este é um exemplo, incluindo
list
´s edata.frame
´s.r
é umlist
doslist
edata.frame
dos (dput(r[[1]]
no final).O objetivo é
unlist
todas as listas, colocando a sequência doslist
nomes como colunas para identificar o caso.Cancele a lista das listas, mas não as
data.frame
.Map
coloca a sequência de nomes como uma coluna.Reduce
junte-se a todosdata.frame
.PS
r[[1]]
:fonte
Digamos que queremos calcular o comprimento de cada elemento.
Se o objetivo é apenas rotular os elementos resultantes, então
lapply(mylist,length)
ou abaixo funciona.Se o objetivo é usar o rótulo dentro da função,
mapply()
é útil fazer um loop sobre dois objetos; os elementos da lista e os nomes da lista.fonte
O @ ferdinand-kraft nos deu um grande truque e depois nos diz que não devemos usá-lo porque não é documentado e por causa da sobrecarga de desempenho.
Não posso discutir muito com o primeiro ponto, mas gostaria de observar que a sobrecarga raramente deve ser uma preocupação.
vamos definir funções ativas para que não tenhamos que chamar a expressão complexa,
parent.frame()$i[]
mas apenas.i()
criaremos.n()
para acessar o nome, que deve funcionar para os funcionais base e purrr (e provavelmente para a maioria também).Agora vamos comparar uma função simples que cola os itens de um vetor em seu índice, usando abordagens diferentes (é claro que essas operações podem ser vetorizadas usando,
paste(vec, seq_along(vec))
mas esse não é o ponto aqui).Definimos uma função de benchmarking e uma função de plotagem e plotamos os resultados abaixo:
Criado em 2019-11-15 pelo pacote reprex (v0.3.0)
A queda no início do primeiro gráfico é um acaso, por favor, ignore-a.
Vemos que a resposta escolhida é realmente mais rápida e, para uma quantidade decente de iterações, nossas
.i()
soluções são realmente mais lentas, a sobrecarga em comparação com a resposta escolhida é cerca de 3 vezes a sobrecarga do usopurrr::imap()
e atinge cerca de 25 ms para iterações de 30k, então eu perco cerca de 1 ms por 1000 iterações, 1 segundo por milhão. Esse é um pequeno custo por conveniência, na minha opinião.fonte
Basta escrever sua própria
lapply
função personalizadaEntão use assim:
fonte