31

我可以使用循环来解决这个问题,但我正在尝试用向量来思考,所以我的代码会更具 R 风格。

我有一个名单。格式为名字_姓氏。我想从这个列表中取出一个只有名字的单独列表。我似乎无法弄清楚如何做到这一点。以下是一些示例数据:

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"

那么我怎么能在没有循环的情况下做到这一点呢?

4

10 回答 10

43

还有一种方法:

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

换句话说,最后一行提取列表中每个组件的第一个元素,然后将其简化为向量。

这是如何运作的?那么,您需要实现另一种写作方式x[1]"["(x, 1),即有一个称为子集的函数[。调用 apply 为原始列表的sapply每个元素调用此函数一次,传入两个参数,列表元素和 1。

这种方法优于其他方法的优点是您可以从列表中提取多个元素,而无需重新计算拆分。例如,姓氏是sapply(pieces, "[", 2)。一旦你习惯了这个习语,它就很容易阅读。

于 2009-08-31T03:20:05.753 回答
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" 

请参阅:R 中“应用”的简要介绍

于 2009-08-31T01:16:25.470 回答
10

怎么样:

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 方法?

于 2009-08-31T02:33:51.370 回答
9

关于什么:

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

sub("_.*", "", t)
于 2010-01-22T18:29:22.177 回答
7

我怀疑这是最优雅的解决方案,但它胜过循环:

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

将列表转换为数据框是我能让它们做我想做的事情的唯一方法。我期待着阅读真正了解如何处理列表的人的答案。

于 2009-08-31T01:12:14.353 回答
4

你几乎拥有它。这真的只是一个问题

  1. 使用其中一个*apply函数来循环你现有的列表,我经常开始,lapply有时切换到sapply
  2. 添加一个匿名函数,一次对列表元素之一进行操作
  3. 你已经知道它是strsplit(string, splitterm)并且你需要奇数[[1]][1]来挑选答案的第一个词
  4. 只需将它们放在一起,从首选变量 namne 开始(因为我们远离torc和朋友)

这使

> 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" 
>
于 2009-08-31T01:09:33.947 回答
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"

可能有更好的方法来仅提取奇数索引条目,但无论如何您都不会有循环。

于 2009-08-31T01:10:16.880 回答
2

还有另一种方法,基于 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]

于 2009-08-31T02:56:25.093 回答
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结构;如果没有,那么此方法将中断。

于 2012-09-07T18:42:07.753 回答
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"
于 2013-02-28T10:53:22.230 回答