2009-08-31 8 views
31

Я мог бы решить это с помощью циклов, но я стараюсь думать в векторах, поэтому мой код будет больше R-esque.Как избежать цикла в R: выбор элементов из списка

У меня есть список имен. Формат имеет имя 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" 

Так как я могу делать это без циклов?

+2

BTW это может быть полезно, если вы могли бы подробно, как это отличается от ваших предыдущих вопросов по той же теме: http://stackoverflow.com/questions/439526/thinking-in -vectors-with-r http://stackoverflow.com/questions/1246244/r-using-the-apply-function-on-a-data-frame-help-me-get-my-vector-victor http://stackoverflow.com/questions/445059/vectorize-my-thinking-vector-operations-in-r –

+4

Вы имеете в виду мою полную неспособность действительно научиться выполнять функции применения в R? Да, такая же проблема, разные нюансы. Спасибо за напоминание. –

ответ

26

Вы можете использовать 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" 

См: A brief introduction to “apply” in R

4

У вас его почти было. It действительно это просто вопрос

  1. с помощью одного из *apply функций в цикле над существующий список, я часто начинаются с lapply и иногда переключаться на sapply
  2. добавить анонимную функцию, которая работает на одном из элементы списка в то время
  3. вы уже знали, что это strsplit(string, splitterm) и что вам нужно нечетное [[1]][1] обрывать первое слагаемое ответа
  4. просто поставить все это вместе, начиная с предпочтительным VARI возможность 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" 
> 
+0

Я действительно изо всех сил пытаюсь правильно сообразить, используя функции приложения в Р. В некоторые дни мне кажется, что учиться ездить на противоположной стороне дороги .. это действительно не сложно, но простые раунды-бои приводят к психическое лог-джем. –

+1

Я делаю это по-ногу. Вы знали strsplit. Вы знали, что вам нужна «функция анона» одного параметра для семейства apply. Просто спрячься вместе ... Наконец, и не забирай, я разместил это до того, как по существу идентичный, но менее подробный ответ, который ты принял как «ответ». –

+0

Typo: «lego-alike», а не «leg-alike» –

3

Вы можете использовать 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" 

Там может быть лучше, чтобы вытащить только записи с нечетным индексом, но в любом случае у вас не будет цикла.

+0

Не идеально, поскольку вам нужно наложить «by = 2», чтобы выбрать соответствующие элементы. –

7

Я сомневаюсь, что это самое элегантное решение, но оно бьется зацикливание:

t.df <- data.frame(tsplit) 
t.df[1, ] 

Преобразование списков для фреймов данных о единственном пути я могу заставить их делать то, что я хочу. Я с нетерпением жду чтения ответов людьми, которые действительно понимают, как обрабатывать списки.

+0

Мне это нравится. Я получаю структуру data.frame. И поскольку мои реальные данные имеют одинаковое количество элементов в каждом «имени», тогда это не должно быть меньше эффективности памяти. Почему я не подумал об этом! –

+0

Обратите внимание, что этот подход занимает много времени с большими данными - см. Мой комментарий к ответу Уильяма Доэна. –

9

Как о:

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?

+1

+1 за то, что он самый быстрый. С rep (t, 1e4) мой подход занял 83,23 секунды (из них 81,41 потрачено на преобразование в кадр данных!), Дэвид взял 4.39s, а ваш взял 0.81. Я думаю, что это тоже самое лучшее. –

+1

Спасибо, Мэтт ... Мне было интересно узнать об эффективности каждого из этих решений! –

+1

Это действительно информативно. Я только что предположил, что бит strsplit был дан. Вау. Хорошо видеть другой способ сделать это. –

2

И один другой подход, основанный на 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]

43

И еще один подход:

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 
pieces <- strsplit(t,"_") 
sapply(pieces, "[", 1) 

В словах, последняя строка извлекает первый элемент каждого компонента списка и затем упрощает его в вектор.

Как это работает? Ну, вам нужно реализовать альтернативный способ написания x[1]: "["(x, 1), то есть функция, называемая [, которая выполняет подмножество. Вызов sapply применяет эту функцию один раз для каждого элемента исходного списка, передавая два аргумента, элемент списка и 1.

Преимущество этого подхода над другими заключается в том, что вы можете извлечь несколько элементов из списка без вынуждены пересчитывать расщепления. Например, фамилия будет sapply(pieces, "[", 2). Как только вы привыкнете к этой идиоме, ее довольно легко прочитать.

+0

Хэдли, я вижу, что это работает, но я не имею ни малейшего понятия, почему это работает. Есть ли подразумеваемый «]»? Можете ли вы немного разобраться? Мой R-foo явно слаб. –

+0

Я тоже был немного в шоке от этого, JD ... поэтому после небольшой игры я вижу, что:> «[» (штук, 1) дает [[1]] [1] «bob» «кузнец». .. интересная нотация, конечно, и очень полезная! –

+0

Как примечание, если вы собираетесь разделить на фиксированные строки вместо регулярных выражений, вам может потребоваться передать 'fixed = TRUE' в' strsplit'. Я обнаружил, что это может сильно повлиять на скорость 'strsplit'. –

9

насчет:

t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan") 

sub("_.*", "", t) 
+0

, что полностью работает! Благодарю. –

1

Я хотел бы использовать следующую 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; если нет, то этот метод будет нарушен.

0

из исходного списка 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" 

 Смежные вопросы

  • Нет связанных вопросов^_^