Как избежать цикла в R: выбор элементов из списка
Я мог бы решить это с помощью циклов, но я стараюсь думать в векторах, поэтому мой код будет больше R-esque.
У меня есть список имен. Формат имеет имя firstname_lastname. Я хочу вывести из этого списка отдельный список только с именами. Я не могу понять, как это сделать. Вот некоторые примеры данных:
t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
tsplit <- strsplit(t,"_")
который выглядит следующим образом:
> tsplit
[[1]]
[1] "bob" "smith"
[[2]]
[1] "mary" "jane"
[[3]]
[1] "jose" "chung"
[[4]]
[1] "michael" "marx"
[[5]]
[1] "charlie" "ivan"
Я мог бы получить то, что хочу, используя такие циклы:
for (i in 1:length(tsplit)){
if (i==1) {t_out <- tsplit[[i]][1]} else{t_out <- append(t_out, tsplit[[i]][1])}
}
который дал бы мне это:
t_out
[1] "bob" "mary" "jose" "michael" "charlie"
Итак, как я могу сделать это без циклов?
Ответы
Ответ 1
Вы можете использовать apply
(или sapply
)
t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
f <- function(s) strsplit(s, "_")[[1]][1]
sapply(t, f)
bob_smith mary_jane jose_chung michael_marx charlie_ivan
"bob" "mary" "jose" "michael" "charlie"
Смотрите: Краткое введение в "применить" в R
Ответ 2
И еще один подход:
t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
pieces <- strsplit(t,"_")
sapply(pieces, "[", 1)
В словах последняя строка извлекает первый элемент каждого компонента списка, а затем упрощает его в вектор.
Как это работает? Ну, вам нужно реализовать альтернативный способ написания x[1]
is "["(x, 1)
, т.е. Существует функция под названием [
, которая выполняет подмножество. Вызов sapply
применяет вызовы этой функции один раз для каждого элемента исходного списка, передавая два аргумента, элемент списка и 1.
Преимущество этого подхода над другими заключается в том, что вы можете извлечь несколько элементов из списка, не перекомпилируя их. Например, фамилия будет sapply(pieces, "[", 2)
. Как только вы привыкнете к этой идиоме, ее довольно легко прочитать.
Ответ 3
Как насчет:
tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
fnames <- gsub("(_.*)$", "", tlist)
# _.* matches the underscore followed by a string of characters
# the $ anchors the search at the end of the input string
# so, underscore followed by a string of characters followed by the end of the input string
для подхода RegEx?
Ответ 4
как насчет:
t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
sub("_.*", "", t)
Ответ 5
Я сомневаюсь, что это самое элегантное решение, но оно бьет:
t.df <- data.frame(tsplit)
t.df[1, ]
Преобразование списков в кадры данных - это единственный способ заставить их делать то, что я хочу. Я с нетерпением жду возможности прочитать ответы людей, которые действительно понимают, как обрабатывать списки.
Ответ 6
У тебя это было почти. Это действительно вопрос
- используя одну из функций
*apply
, чтобы перебрать ваш существующий список, я часто начинаю с lapply
и иногда переключаюсь на sapply
- добавить анонимную функцию, которая работает с одним из элементов списка за раз
- вы уже знали, что это был
strsplit(string, splitterm)
, и вам нужно нечетное [[1]][1]
, чтобы выбрать первый термин ответа
- просто поместите все это вместе, начиная с предпочтительной переменной namne (поскольку мы остаемся в стороне от
t
или c
и друзей)
который дает
> tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
> fnames <- sapply(tlist, function(x) strsplit(x, "_")[[1]][1])
> fnames
bob_smith mary_jane jose_chung michael_marx charlie_ivan
"bob" "mary" "jose" "michael" "charlie"
>
Ответ 7
Вы можете использовать unlist()
:
> tsplit <- unlist(strsplit(t,"_"))
> tsplit
[1] "bob" "smith" "mary" "jane" "jose" "chung" "michael"
[8] "marx" "charlie" "ivan"
> t_out <- tsplit[seq(1, length(tsplit), by = 2)]
> t_out
[1] "bob" "mary" "jose" "michael" "charlie"
Может быть лучший способ вытащить только записи с нечетным индексом, но в любом случае у вас не будет цикла.
Ответ 8
И еще один подход, основанный на примере unlist brentonk...
tlist <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
tsplit <- unlist(strsplit(tlist,"_"))
fnames <- tsplit[seq(1:length(tsplit))%%2 == 1]
Ответ 9
Я бы использовал следующий метод unlist():
> t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
> tsplit <- strsplit(t,"_")
>
> x <- matrix(unlist(tsplit), 2)
> x[1,]
[1] "bob" "mary" "jose" "michael" "charlie"
Большим преимуществом этого метода является то, что он одновременно разрешает эквивалентную задачу для фамилий:
> x[2,]
[1] "smith" "jane" "chung" "marx" "ivan"
Недостатком является то, что вам нужно быть уверенным, что все имена соответствуют структуре firstname_lastname
; если нет, то этот метод будет нарушен.
Ответ 10
из исходного объекта tsplit
, заданного в начале, эта команда будет выполнять:
unlist(lapply(tsplit,function(x) x[1]))
он извлекает первый элемент всех элементов списка, затем преобразует список в вектор. Отказываясь сначала к матрице, тогда извлечение первого столбца также нормально, но тогда вы зависите от того факта, что все элементы списка имеют одинаковую длину. Вот результат:
> tsplit
[[1]]
[1] "bob" "smith"
[[2]]
[1] "mary" "jane"
[[3]]
[1] "jose" "chung"
[[4]]
[1] "michael" "marx"
[[5]]
[1] "charlie" "ivan"
> lapply(tsplit,function(x) x[1])
[[1]]
[1] "bob"
[[2]]
[1] "mary"
[[3]]
[1] "jose"
[[4]]
[1] "michael"
[[5]]
[1] "charlie"
> unlist(lapply(tsplit,function(x) x[1]))
[1] "bob" "mary" "jose" "michael" "charlie"