Есть ли способ получить имя индекса списка в моей функции lapply ()?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })
Я спросил , прежде чем , если это возможно , чтобы сохранить имена индексов в lapply () возвращенный список, но я до сих пор не знаю , если есть простой способ извлечь имя каждого элемента внутри пользовательской функции. Я хотел бы избежать вызова lapply для самих имен, я бы предпочел получить имя в параметрах функции.
Ответы:
К сожалению,
lapply
только дает вам элементы вектора, который вы передаете. Обычный обходной путь - передать ему имена или индексы вектора вместо самого вектора.Но обратите внимание, что вы всегда можете передать дополнительные аргументы функции, поэтому работает следующее:
Здесь я использую
lapply
над индексамиx
, а также передатьx
и именаx
. Как видите, порядок аргументов функции может быть любым -lapply
передается в «элементе» (здесь индекс) первому аргументу, не указанному среди дополнительных. В этом случае я указываюy
иn
, так что осталось толькоi
...Который производит следующее:
ОБНОВЛЕНИЕ Более простой пример, тот же результат:
Здесь функция использует «глобальную» переменную
x
и извлекает имена в каждом вызове.источник
y
вместо того,x
чтобы (надеюсь) было понятнее, что функция может вызывать свои аргументы как угодно. Также изменены векторные значения на11,12,13
.Это в основном использует тот же обходной путь, что и Tommy, но при
Map()
этом нет необходимости обращаться к глобальным переменным, которые хранят имена компонентов списка.Или, если вы предпочитаете
mapply()
источник
mapply()
обратите внимание наSIMPLIFY
параметр, который по умолчанию имеет значение true. В моем случае это превратило все это в большую матрицу, когда я хотел применить только простой список. УстановкаF
(внутриmapply()
) заставила его работать как задумано.ОБНОВЛЕНИЕ для версии R 3.2
Отказ от ответственности: это хакерский трюк, и может перестать работать в следующих выпусках.
Вы можете получить индекс, используя это:
Примечание:
[]
для того, чтобы это работало, требуется, так как это заставляет R думать, что символi
(находящийся в рамке оценкиlapply
) может иметь больше ссылок, тем самым активируя его ленивое дублирование. Без этого R не будет хранить отдельные копииi
:Другие экзотические трюки могут быть использованы, как
function(x){parent.frame()$i+0}
илиfunction(x){--parent.frame()$i}
.Влияние на производительность
Приведет ли принудительное дублирование к снижению производительности? Да! вот критерии:
Вывод
Этот ответ просто показывает, что вы НЕ должны использовать это ... Мало того, что ваш код будет более читабельным, если вы найдете другое решение, такое как Tommy выше, и более совместимым с будущими выпусками, вы также рискуете потерять оптимизации, над которыми работала основная команда развиваться!
Трюки старых версий, больше не работают:
Результат:
Объяснение:
lapply
создает вызовы формыFUN(X[[1L]], ...)
иFUN(X[[2L]], ...)
т. Д. Таким образом, аргумент, который он передает, этоX[[i]]
гдеi
текущий индекс в цикле. Если мы получим это до того, как оно будет оценено (т.е. если мы его используемsubstitute
), мы получим неоцененное выражениеX[[i]]
. Это вызов[[
функции с аргументамиX
(символ) иi
(целое число). Такsubstitute(x)[[3]]
возвращает именно это целое число.Имея индекс, вы можете получить простой доступ к именам, если сначала сохраните его так:
Результат:
Или используя этот второй трюк: :-)
(результат тот же).
Объяснение 2:
sys.call(1)
возвращаетlapply(...)
, так чтоsys.call(1)[[2]]
это выражение используется в качестве аргумента списка дляlapply
. Передача этогоeval
создает легитимный объект, к которомуnames
можно получить доступ. Хитро, но это работает.Бонус: второй способ получить имена:
Обратите внимание, что
X
это допустимый объект в родительском фреймеFUN
и ссылается на аргумент спискаlapply
, так что мы можем получить его с помощьюeval.parent
.источник
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
возвращает все 3. Не могли бы вы объяснить, как была выбрана эта 3? а причина расхождения? Она равна длине списка, в данном случае 3. Извините, если это основной вопрос, но хотел бы знать, как применить его в общем случае.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
работает ... Я проверю, что происходит.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
больше не работает и выдает ошибку,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
есть ли простой способ исправить это?У меня была такая же проблема много раз ... Я начал использовать другой способ ... Вместо того, чтобы использовать
lapply
, я начал использоватьmapply
источник
Вы можете попробовать использовать
imap()
изpurrr
пакета.Из документации:
Итак, вы можете использовать это так:
Что даст вам следующий результат:
источник
Просто зациклите имена.
источник
mylist
внутри функции - плохая практика . Лучше еще сделатьfunction(mylist, nm) ...
Ответ Томми относится к именованным векторам, но я понял, что вас интересуют списки. И кажется, что он делал обход, потому что он ссылался на «х» из вызывающей среды. Эта функция использует только параметры, которые были переданы в функцию, и поэтому не делает никаких предположений относительно имени объектов, которые были переданы:
источник
NULL
? Так чтоlapply(x, function(x) NULL)
дает тот же ответ ...lapply
всегда добавляет имена изx
к результату впоследствии .Мой ответ идет в том же направлении, что и у Томми и Каракалов, но избегает необходимости сохранять список как дополнительный объект.
Результат:
Это дает список в качестве именованного аргумента для FUN (вместо lapply). lapply должен только перебирать элементы списка (будьте осторожны, чтобы изменить этот первый аргумент на lapply при изменении длины списка).
Примечание: Предоставление списка непосредственно для lapply в качестве дополнительного аргумента также работает:
источник
И @caracals, и @Tommy - хорошие решения, и это пример, включающий
list
´s иdata.frame
´s.r
являетсяlist
изlist
´s иdata.frame
´s (dput(r[[1]]
в конце).Цель состоит во
unlist
всех списках, помещая последовательностьlist
имен в виде столбцов, чтобы идентифицировать случай.Unlist списки, но не в
data.frame
.Map
помещает последовательность имен в виде столбца.Reduce
присоединиться ко всемdata.frame
.PS
r[[1]]
:источник
Допустим, мы хотим рассчитать длину каждого элемента.
Если цель состоит в том, чтобы просто пометить результирующие элементы, то
lapply(mylist,length)
или ниже работает.Если цель состоит в том, чтобы использовать метку внутри функции, тогда
mapply()
полезно, зацикливаясь на двух объектах; элементы списка и имена списков.источник
@ ferdinand-kraft дал нам отличный трюк, а затем сказал, что мы не должны его использовать, потому что он недокументирован и из-за снижения производительности.
Я не могу много спорить с первым пунктом, но я хотел бы отметить, что накладные расходы редко должны вызывать беспокойство.
Давайте определим активные функции, чтобы нам не нужно было вызывать сложное выражение,
parent.frame()$i[]
а только..i()
Мы также создадим.n()
для доступа к имени, которое должно работать как для базовых, так и для мурлоковых функционалов (и, вероятно, большинства других).Теперь давайте протестируем простую функцию, которая вставляет элементы вектора в их индекс, используя разные подходы (эти операции, конечно, могут быть векторизованы с использованием,
paste(vec, seq_along(vec))
но здесь дело не в этом).Мы определяем функцию бенчмаркинга и функцию построения графика и выводим результаты ниже:
Создано в 2019-11-15 пакетом представлением (v0.3.0)
Падение в начале первого графика - случайность, пожалуйста, игнорируйте его.
Мы видим, что выбранный ответ действительно быстрее, и при приличном количестве итераций наши
.i()
решения действительно медленнее, накладные расходы по сравнению с выбранным ответом примерно в 3 раза превышают накладные расходы на использованиеpurrr::imap()
и составляют примерно 25 мс для 30 тыс. Итераций, поэтому я теряю около 1 мс на 1000 итераций, 1 с на миллион. Это небольшая цена для удобства, на мой взгляд.источник
Просто напишите свою собственную
lapply
функциюЗатем используйте как это:
источник