首页 > 解决方案 > 如何编写 R 函数以根据两个现有列的条件值在任何数据框中创建新列?

问题描述

问题:

我有一个相当令人沮丧的问题。我正在尝试编写一个函数,该函数将根据该数据帧的两个现有列中的值在任何给定数据帧中创建/填充一个新列。

语境:

我经常使用引用具有两个维度(项目 ID (1-7) 和 localeID(de_DE、fr_FR、jp_JP 等)的事件的数据库。

projectId    localeId
1            jp_JP   
2            es_ES       
3            de_DE         
1            jp_JP       
2            es_ES          
3            de_DE

我必须从数据库执行 ETL 并创建一个基于 projectId 和 localeID 的新“市场”维度。例如,projectId 为 1 和 localeId 为 jp_JP 可能意味着市场是“JAPAN1”。

 projectId localeId   market
         1    jp_JP   JAPAN1
         2    es_ES   SPAIN2
         3    au_AU     AUS3
         4    us_US      US4
         5    en_EN ENGLAND5
         6    de_DE GERMANY6

当前成功代码:

现在写,我有一段冗长的使用 ifelse 函数的 R 代码。IE...

df$market <- ifelse(df$localeId == "jp_JP" & df$projectId == '1', "JAPAN1")
df$market <- ifelse(df$localeId == "es_ES" & df$projectId == '10', "SPAIN10")

这可以。它完成了工作。不幸的是,我有很多处理这个市场功能的脚本,我不想一遍又一遍地复制粘贴这个 ifelse 代码。相反,我想编写一个可用于任何数据框的函数,以根据 localeId 和 projectId 创建一个新的市场列。

尝试/失败的解决方案:

market_names <- function(df, "market", "projectId", "localeId"){
             df$market <- NA 
             x <- ifelse(projectId == 1, "1",
                     ifelse(projectId == 2, "2", projectId)
             y <- ifelse(localeId == "jp_JP", "JAPAN",
                     ifelse(localeId == "es_ES", "SPAIN", localeId)
            for(i in 1:nrow(df)){
                 df[i,]$market <- paste(x,y, sep = "")
}

df <- market_names(df, "market", "projectId", "localeId")

不幸的是,当我尝试将数据帧传递给函数时,代码就崩溃了,甚至不会运行。

要求:

我希望这里有人遇到过类似的问题,并且可以就如何修复此功能提供建议,以便它可以随时用于任何数据帧。我的 projectId 和 localeId 的维度总是相同的,所以我认为这会在将来为我节省很多时间。

如果您认为有一种更简单的方法可以在没有功能的情况下完成所有这些操作,我也很想听听您的想法!

提前致谢!

标签: rfunction

解决方案


由于多种原因,您的功能不起作用。例如,您缺少ifelses 上的右括号。函数定义上的右大括号也丢失了。for 循环中的xandy变量是长度 > 1 的向量,因此它们也需要索引。参数不应该是单独的字符串,它们应该分配给可以在函数中引用的变量。您的嵌套ifelses 仅处理两种情况:1 和 2,以及 JAPAN 和 SPAIN。最后,您的函数不返回任何内容。修复这些东西后,你最终会得到这个:

market_names <- function(df){
    df$market <- NA
    x <- ifelse(df$projectId == 1, "1",
                ifelse(df$projectId == 2, "2", df$projectId))
    y <- ifelse(df$localeId == "jp_JP", "JAPAN",
                ifelse(df$localeId == "es_ES", "SPAIN", df$localeId))
    for(i in 1:nrow(df)){
        df[i,]$market <- paste(x[i],y[i], sep = "")
    }
    df
}

market_names(df)

#### OUTPUT ####

# A tibble: 6 x 3
  projectId localeId market
      <dbl> <chr>    <chr> 
1         1 jp_JP    1JAPAN
2         2 es_ES    2SPAIN
3         3 au_AU    3au_AU
4         4 us_US    4us_US
5         5 en_EN    5en_EN
6         6 de_DE    6de_DE

很明显x应该在为什么y在之后paste。您的条件还需要更好地处理所有情况。

只使用映射到国家名称的国家代码列表可能是最简单的。然后可以将该列localeId用作索引。循环和条件都不是必需的:

# Country proper names can be accessed using codes.
country_codes <- list("jp_JP" = "JAPAN",
                      "es_ES" = "SPAIN",
                      "de_DE" = "GERMANY",
                      "au_AU" = "AUS",
                      "us_US" = "US",
                      "en_EN" = "ENGLAND",
                      "de_DE" = "GERMANY"
                      )

# Pass in dataframe and country codes.
market_names <- function(df, country_codes){
    df$market <- paste0(unlist(country_codes[df$localeId], use.names = F),
                        df$projectId
                        )
    df
}

# Function call:
market_names(df, country_codes)

#### OUTPUT ####

# A tibble: 6 x 3
  projectId localeId market  
      <dbl> <chr>    <chr>   
1         1 jp_JP    JAPAN1  
2         2 es_ES    SPAIN2  
3         3 au_AU    AUS3    
4         4 us_US    US4     
5         5 en_EN    ENGLAND5
6         6 de_DE    GERMANY6

推荐阅读