【问题标题】:removing rows based on two conditions from a data.table in R从 R 中的 data.table 中删除基于两个条件的行
【发布时间】:2020-04-11 09:41:46
【问题描述】:

我有一个data.table,带有国家代码和相应的国家名称。在某些情况下,国家/地区未知,"OTHER" 用于指示未知代码。我的数据不一致,因为对于某些code,我有country 名称,但也有"OTHER", 的一行,例如IRLANDLUXEMBURG

code <- c(104, 105, 105, 106, 109, 112, 115, 115)
country <- c("GERMANY", "IRLAND", "OTHER", "FRANCE", "FRANCE", "ITALY",  "OTHER", "LUXEMBURG")
id_country <- cbind(code, country)
id_country <- as.data.table(id_country)

我想做的事:我想获得唯一的code - 对于每个代码,只有一行,最好是国家名称,如果不可用,则使用“其他”。我正在寻找最简单的解决方案。

现在我想首先检查我的data.table 是否有一些不一致的地方。如果是,则删除 country 列中同时具有 - 国家名称和“其他”的所有行。我尝试了以下方法,但没有一个 duplicates 被删除

if (length(unique(id_country$code)) != length(unique(id_country))){

  # replace "OTHER" with the corresponding country name 
  duplicates <- id_country[duplicated(code),]
  id_country <- id_country[!(id_country$code %in% duplicates & id_country$country == "OTHER"),]

}

想要的输出:

code <- c(104, 105, 106, 109, 112, 115)
country <- c("GERMANY", "IRLAND", "FRANCE", "FRANCE", "ITALY", "LUXEMBURG")
id_country <- cbind(code, country)
id_country <- as.data.table(id_country)

【问题讨论】:

  • 当 2 个代码(例如 106 和 109)的名称为 country, France 时会发生什么?

标签: r data.table


【解决方案1】:

如果您愿意使用dplyr,这里还有一个选项:

code_n 计算code 中的重复次数,ifelse 有条件地填充country 变量,同时用NaN 替换任何“其他”条目以表示重复的codeNaN 条目稍后会被过滤掉。如果有一个带有“OTHER”作为国家过滤的唯一代码条目,该代码也可以工作!=“OTHER”在这种情况下不起作用。

id_country %>% group_by (code) %>% mutate(code_n=n()) %>% mutate(country = ifelse(code_n == 1,country,ifelse(country!="OTHER",country,NaN))) %>% filter(country!=NaN) %>% select(-code_n)

输出

小数点:6 x 2

# Groups:   code [6]
  code  country  
  <chr> <chr>    
1 104   GERMANY  
2 105   IRLAND   
3 106   FRANCE   
4 109   FRANCE   
5 112   ITALY    
6 115   LUXEMBURG

【讨论】:

    【解决方案2】:

    我有两个解决方案。它们都将产生相同的结果。如果您有大量数据,第二个可能更合适,因为它避免了合并。

    在您的示例中,没有观察到只有一个代码与OTHER 相关联。两种方法都不会修改这种观察结果。

    解决方案 1

    第一个基于merge。想法是清理只包含代码的data.table,然后与初始数据合并

    # METHODE 1: MERGE
    id_country2 <- id_country[,.('clean_code' = unique(country)), by = code]
    id_country2[, 'number_codes' := .N, by = code]
    id_country2 <- id_country2[!(number_codes == 2 & clean_code == "OTHER")]
    
    merge(id_country, id_country2)
    
       code number_codes   country clean_code
    1:  104            1   GERMANY    GERMANY
    2:  105            2    IRLAND     IRLAND
    3:  105            2     OTHER     IRLAND
    4:  106            1    FRANCE     FRANCE
    5:  109            1    FRANCE     FRANCE
    6:  112            1     ITALY      ITALY
    7:  115            2     OTHER  LUXEMBURG
    8:  115            2 LUXEMBURG  LUXEMBURG
    

    解决方案 2

    第二种解决方案直接在初始数据帧中使用条件替换。这个想法是在执行替换之前创建一个函数,然后仅将其应用于某些代码。

    函数是:

    replace_country <- function(x){
      val <- unique(x)
      return(
         gsub(pattern = "OTHER", replacement = val[val != "OTHER"][1],
              x)
    ) 
    }
    

    可能有更优雅的方式来定义它,但它会完成这项工作。顺便说一句,我输入了val[val != "OTHER"][1] 以确保您只输入一个值来替换。这可能是额外的小心,但以防万一。

    这个函数将使用lapply+SD动词调用

    id_country[, 'number_codes' := uniqueN(country), by = "code"]
    id_country[number_codes > 1,  country := lapply(.SD, replace_country), .SDcols = "country",
               by = "code"]
    
    
      code   country number_codes
    1:  104   GERMANY            1
    2:  105    IRLAND            2
    3:  105    IRLAND            2
    4:  106    FRANCE            1
    5:  109    FRANCE            1
    6:  112     ITALY            1
    7:  115 LUXEMBURG            2
    8:  115 LUXEMBURG            2
    

    您只能将replace_country 函数应用于number_codes>1 的观测值,使用此语法。您的数据框通过引用直接更新

    【讨论】:

      【解决方案3】:

      我们可以用if检查条件:

      library(data.table)
      
      id_country[, .(country = if(any(country != 'OTHER')) 
                              country[country != 'OTHER'][1L] else 'OTHER'), code]
      
      #   code   country
      #1:  104   GERMANY
      #2:  105    IRLAND
      #3:  106    FRANCE
      #4:  109    FRANCE
      #5:  112     ITALY
      #6:  115 LUXEMBURG
      

      【讨论】:

        【解决方案4】:

        如果希望消除带有OTHER 的项目并且所有标记为OTHER 的项目与另一个国家名称重复,我们可以简单地选择国家不等于OTHER 的行。

        library(data.table)
        code <- c(104, 105, 105, 106, 109, 112, 115, 115)
        country <- c("GERMANY", "IRLAND", "OTHER", "FRANCE", "FRANCE", "ITALY",  "OTHER", "LUXEMBURG")
        id_country <- cbind(code, country)
        id_country <- as.data.table(id_country)
        
        id_country[country != "OTHER",]
        

        如果列表中有“有效”的未知国家(即名称为OTHER的不重复国家代码,则解决方案会稍微复杂一些。

        首先,我们将使用有效的OTHER,国家/地区117修改输入数据后找到重复的国家/地区。

        library(data.table)
        code <- c(104, 105, 105, 106, 109, 112, 115, 115,117)
        country <- c("GERMANY", "IRLAND", "OTHER", "FRANCE", "FRANCE", "ITALY",  "OTHER", 
                     "LUXEMBURG","OTHER")
        id_country <- cbind(code, country)
        id_country <- as.data.table(id_country)
        dupCodes <- id_country[, 'count' := .N, by = code][count > 1,.SD[1],by = code][[1]]
        

        然后,我们将只删除countryOTHER 并且有code 重复的行。

        id_country[country != "OTHER" | !(code %in% dupCodes),]
        

        ...和输出:

        > id_country[country != "OTHER" | !(code %in% dupCodes),]
           code   country
        1:  104   GERMANY
        2:  105    IRLAND
        3:  106    FRANCE
        4:  109    FRANCE
        5:  112     ITALY
        6:  115 LUXEMBURG
        7:  117     OTHER
        > 
        

        纠正原来的编码错误

        正如最初编写的那样,问题帖子中的代码包含一个细微的错误,导致最终的子集操作总是失败。

         duplicates <- id_country[duplicated(code),]
        

        由于 duplicatesdata.table,而不是数字向量,因此以下代码片段的计算结果始终为 FALSE。

        id_country$code %in% duplicates
        

        这段代码在if() 块内的事实使得很难看出duplicates 是一个数据表,因为我们在RStudio 环境查看器中看不到它。如果我在 if() 块之外运行代码块并检查对象,我会看到以下内容。

        显然duplicates 不是向量。

        这个问题会导致其余的子集操作从输入数据表中返回每一行。

        id_country <- id_country[!(id_country$code %in% duplicates & id_country$country == "OTHER"),]
        

        为什么?

        id_country$code %in% duplicates
        > id_country$code %in% duplicates
        [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
        >
        

        我们可以通过从进行duplicates 赋值的代码行中返回向量而不是数据表来纠正缺陷,如下所示。

        if (length(unique(id_country$code)) != length(unique(id_country))){
        
             # extract first column of resulting data.table as a vector
             duplicates <- id_country[duplicated(code),][[1]]
             # subset out duplicate rows named OTHER
             id_country <- id_country[!(id_country$code %in% duplicates & id_country$country == "OTHER"),]
        
        }
        id_country
        

        ...和输出:

        > id_country
           code   country
        1:  104   GERMANY
        2:  105    IRLAND
        3:  106    FRANCE
        4:  109    FRANCE
        5:  112     ITALY
        6:  115 LUXEMBURG
        > 
        

        【讨论】:

        • 所以我的代码中需要的只是将id_country[!(id_country$code %in% duplicates &amp; id_country$country == "OTHER"),] 更改为id_country[!(id_country$code %in% duplicates | id_country$country == "OTHER"),]
        • ...不完全是。您的代码由于一个细微的错误而失败。正如所写,duplicates 是一个数据框,而不是一个向量。因此,id_country$code %in% duplicates 的计算结果始终为 FALSE,因此子集操作永远不会从结果中删除行。我将更新我的答案以解释如何更正代码。
        猜你喜欢
        • 2022-06-10
        • 2014-05-04
        • 2021-03-06
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 2014-07-18
        • 1970-01-01
        • 2021-10-23
        相关资源
        最近更新 更多