【问题标题】:How to avoid a loop in R: selecting items from a list如何避免 R 中的循环:从列表中选择项目
【发布时间】:2010-11-24 05:31:58
【问题描述】:

我可以使用循环来解决这个问题,但我正在尝试用向量来思考,所以我的代码会更加 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"

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

【问题讨论】:

标签: list r vector strsplit


【解决方案1】:

你几乎拥有它。 真的只是

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

给了

> 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" 
>

【讨论】:

  • 我真的很难用 R 中的 apply 函数正确地思考问题。有些日子感觉就像在马路的另一边学习开车......这真的不难,但很简单-a-bouts 会导致精神日志堵塞。
  • 我以类似腿的方式来做。你知道strsplit。您知道您需要应用系列的一个参数的“匿名函数”。把它们粘在一起......最后,而不是挑剔,我在你接受的基本相同但不太冗长的答案之前发布了这个答案。
  • 错字:'lego-alike',而不是'leg-alike'
  • Dirk,作为 R 的新手,我注意到的一件事是很难看出两个给定的问题是相似的。我认为专业知识带来了快速选择有意义的类比的能力。我正在慢慢地到达我可以看到模式的地方。我很欣赏你上面关于弄清楚乐高积木是什么的评论。例如,我查看问题并发现我需要匿名函数的能力仍在增长。
【解决方案2】:

你可以使用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"

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

【讨论】:

  • 不理想,因为您需要强加 'by = 2' 来选择匹配的元素。
【解决方案3】:

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

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

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

【讨论】:

  • 我喜欢这个。我'得到' data.frame 结构。而且由于我的真实数据在每个“名称”中具有相同数量的项目,因此这应该不会降低内存效率。我怎么没想到呢!
  • 请注意,这种方法需要很长时间才能处理大量数据 - 请参阅我对 William Doane 回答的评论。
【解决方案4】:

您可以使用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

【讨论】:

    【解决方案5】:

    怎么样:

    tlist &lt;- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
    fnames &lt;- 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 表示最快。使用 rep(t, 1e4),我的方法用了 83.23 秒(其中 81.41 用于转换为数据帧!),David 用了 4.39s,而你用了 0.81。我认为它也有最好的输出。
    • 谢谢,Matt... 我想知道这些解决方案的效率!
    • 这真的很丰富。我刚刚假设 strsplit 位是给定的。哇。很高兴看到另一种方法。
    【解决方案6】:

    还有另一种方法,基于 brentonk 的 unlist 示例...

    tlist &lt;- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
    tsplit &lt;- unlist(strsplit(tlist,"_"))
    fnames &lt;- tsplit[seq(1:length(tsplit))%%2 == 1]

    【讨论】:

      【解决方案7】:

      还有一种方法:

      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)。一旦你习惯了这个成语,它就会很容易阅读。

      【讨论】:

      • Hadley,我看到这行得通,但我一点也不知道为什么行得通。是否有隐含的“]”?你能详细说明一下吗?我的 R-foo 显然很弱。
      • 我也有点震惊,JD...所以玩了一会儿,我看到: > "["(pieces,1) 产生 [[1]] [1] "bob" "smith" ... 一个有趣的符号,可以肯定的是,非常有用!
      • 附带说明,如果您要拆分固定字符串而不是正则表达式,您可能需要考虑将fixed=TRUE 传递给strsplit。我发现这会对strsplit 的速度产生很大影响。
      • R 中的所有运算符都是函数 - 中缀运算符可以用前缀表示法编写。真的|| FALSE可以写成||(TRUE,FALSE),a[b]可以写成[(a,b),甚至赋值运算符a[b] [<-(a,b ,值=真)。 R 是魔法。
      • 不确定它是否正确输出,但前缀函数周围应该有引号(我使用了反引号,但常规引号也应该起作用)。
      【解决方案8】:

      怎么样:

      t <- c("bob_smith","mary_jane","jose_chung","michael_marx","charlie_ivan")
      
      sub("_.*", "", t)
      

      【讨论】:

        【解决方案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"
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-04-18
            • 2021-08-10
            • 1970-01-01
            • 1970-01-01
            • 2011-03-20
            • 1970-01-01
            相关资源
            最近更新 更多