【问题标题】:Change multiple elements in R vector with repeated names使用重复名称更改 R 向量中的多个元素
【发布时间】:2017-08-22 14:32:46
【问题描述】:

假设我有一个名称重复的向量:

x <- c(a=1, b=2, a=3, c=4, c=5, b=1, d=1)

我想查找和更改命名元素。如果我定义

ElementsToChange <- c("a","b","c")

ChangeTo <- c(9,8,7)

如果我这样做,我想将所有名为“a”的元素更改为 9,所有名为“b”的元素更改为 8,等等:

x[ElementsToChange] <- ChangeTo

这只会更改第一个(而不是所有)元素。

我如何以一种简单而优雅的方式改变一切?

【问题讨论】:

  • 另一个是v1 &lt;- setNames(setNames(ChangeTo, ElementsToChange)[names(x)], names(x));v1[is.na(v1)] &lt;- x[is.na(v1)]
  • @Frank 我的意思是“嘿,这个问题有帮助”;-)

标签: r names


【解决方案1】:

您可以使用单个 split* 命令来完成:

split(x, names(x))[ElementsToChange] <- ChangeTo
#x
#a b a c c b d 
#9 8 9 7 7 8 1 

这首先将 x 向量按其名称拆分,然后对属于 ElementsToChange 向量的所有元素进行子集化,并将这些值替换为 ChangeTo 值。

* 从技术上讲,您在这里使用split&lt;-,即对拆分数据进行分配。原始向量之后保持其结构。

【讨论】:

  • 这是一个绝妙的解决方案。从来没想过你可以在拆分后分配!
【解决方案2】:

我假设您不需要保留任何匹配项,这会使其不那么优雅:

ifelse(names(x)%in%ElementsToChange,ChangeTo[match(names(x),ElementsToChange)],x)
[1] 9 8 9 7 7 8 1

如果您想恢复名称,则必须重命名向量。

【讨论】:

  • 或者类似地没有 ifelse,m &lt;- match(names(x), ElementsToChange); replace(x, !is.na(m), ChangeTo[na.omit(m)])
  • @Frank,为什么不:x[!is.na(m)] &lt;- ChangeTo[na.omit(m)] 而不是替换?
  • @MikeH 是的。查看replace 的代码,它们本质上是相同的。 replaceifelse 一样,创建一个新对象,而您的方式修改 x(这更接近 OP 想要的)。
  • @Frank,我觉得这是最完整/最简单的解决方案,所以我用它更新了我的答案。由于您完成了大部分工作,尽管您可以随意发布它,我可以恢复到我原来的(更糟糕的)解决方案。
【解决方案3】:

当名称是唯一的时,命名元素会更好地工作,但你可以这样做

Reduce(function(x, z) {
  x[names(x)==z$k] <- z$v
  x
}, split(data.frame(k=ElementsToChange, v=ChangeTo), seq_along(ChangeTo)), init=x)
# a b a c c b d 
# 9 8 9 7 7 8 1

但实际上,在 data.frame 中使用正确的键/值会更容易。

library(tidyverse)
dd <- data_frame(k=names(x), v=x)
tt <- data_frame(k=ElementsToChange, newv=ChangeTo)
dd %>% left_join(tt) %>% mutate(v=coalesce(newv, v), newv=NULL)

【讨论】:

    【解决方案4】:

    另一个选项直接替换为&lt;-,感谢@Frank:

    m <- match(names(x), ElementsToChange)
    x[!is.na(m)] <- ChangeTo[na.omit(m)]
    
    x
    a b a c c b d 
    9 8 9 7 7 8 1 
    

    由于它直接替换它,因此它也保留了名称。您将x 子集化为仅ElementsToChange 中的名称,然后将它们的值替换为ChangeTo 中的匹配元素

    【讨论】:

      【解决方案5】:
      ids = match(names(x), ElementsToChange)
      replace(x, which(!is.na(ids)), ChangeTo[ids[!is.na(ids)]])
      #a b a c c b d 
      #9 8 9 7 7 8 1 
      

      【讨论】:

        【解决方案6】:

        您可以使用此函数并提供您的 elementsToChange 和 changeTo 向量来获取更改后的列表。

        library(dplyr)
        
        change <- function(x, elementsToChange = c("a"), changeTo=c(5)){
        
          if(length(elementsToChange)!=length(changeTo)) {stop("length of elementsToChange is not equal to changeTo")}
          temp<- tibble(name = names(x), value = x)
          temp.change<- tibble(name = elementsToChange, value = changeTo)
          temp2<-temp%>%left_join(temp.change, by = "name")
          temp2<-within(temp2, {
            value.y[is.na(value.y)]<- value.x[is.na(value.y)]
          })
          ret<-temp2$value.y
          names(ret)<-temp2$name
          return(ret)
        }
        

        【讨论】:

          猜你喜欢
          • 2019-02-27
          • 1970-01-01
          • 2016-06-08
          • 1970-01-01
          • 2021-03-31
          • 1970-01-01
          • 2017-05-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多