R:使用字典/列表转换数据框列?

时间:2018-12-05 00:26:52

标签: r dataframe transformation

我有一个数据框列,其中包含许多文本值(级别)。我需要将这些值映射到预定义的类似对象的结构,以减少级别数。我可以使用字典在Python中轻松实现这一点,但对R中的列表却无法做到这一点。

例如,我的dataframe列类似于:

df <- data.frame(weather = c('Clear','Snow','Clear','Rain','Rain','Other','Hail/sleet','Unknown'))

我需要将此映射到类似

的列表
weather.levels <- list(
  dry = c('Clear', 'Cloudy'),
  wet = c('Snow', 'Rain', 'Hail/sleet'),
  other = c('Other','Unknown'))

这样我的变换后的数据框看起来就像

    old.weather new.weather
1       Clear         dry
2        Snow         wet
3       Clear         dry
4        Rain         wet
5        Rain         wet
6      Other1       other
7  Hail/sleet         wet
8     Unknown       other

我看过诸如thisthis之类的解决方案,但是这些解决方案无法回答我的问题。我无法创建使用R的match函数的数据框,因为预设字典weather.levels的每个类别(“干”,“湿”,“其他”)中的级别数不同。

5 个答案:

答案 0 :(得分:3)

正如通常那样,有一个基本的R函数被设计用来精确地做到这一点。 levels<-是您想要的:

df$new.weather <- `levels<-`(df$weather, weather.levels)
df
#     weather new.weather
#1      Clear         dry
#2       Snow         wet
#3      Clear         dry
#4       Rain         wet
#5       Rain         wet
#6      Other       other
#7 Hail/sleet         wet
#8    Unknown       other

以稍长但更易于阅读的形式等效于:

df$new.weather <- df$weather
levels(df$new.weather) <- weather.levels

答案 1 :(得分:1)

这是使用dplyr-

的一种方法
weather.levels %>% 
  unlist() %>% 
  data_frame(new.weather = gsub("[0-9]", "", names(.)), old.weather = .) %>% 
  left_join(df, ., by = c("weather" = "old.weather"))

     weather new.weather
1      Clear         dry
2       Snow         wet
3      Clear         dry
4       Rain         wet
5       Rain         wet
6      Other       other
7 Hail/sleet         wet
8    Unknown       other

答案 2 :(得分:1)

有三种简单的方法。首先,我将略微修改数据(删除“其他”)以突出显示其中一种方法的优势。

df <- data.frame(weather = c('Clear','Snow','Clear','Rain','Rain','Other','Hail/sleet','Unknown'))
weather.levels <- list(
  dry = c('Clear', 'Cloudy'),
  wet = c('Snow', 'Rain', 'Hail/sleet'),
  other = c('Unknown'))

简单查找

levels1 <- c(Unknown="other",Snow="wet",Rain="wet","Hail/sleet"="wet",Clear="dry",Cloudy="dry")
### levels1 <- setNames(rep(names(weather.levels), lengths(weather.levels)), unlist(weather.levels))
transform(df, newwx = levels1[as.character(weather)])
#      weather newwx
# 1      Clear   dry
# 2       Snow   wet
# 3      Clear   dry
# 4       Rain   wet
# 5       Rain   wet
# 6      Other  <NA>
# 7 Hail/sleet   wet
# 8    Unknown other

(我使用的是基数为R的transform,但是如果您感到更舒服,也可以轻松使用dplyr等。)

表合并

从本质上讲,这就是Shree的回答(尽管概念不只是dplyr和朋友)。

df2 <- data.frame(wxfrom = names(levels1), wxto = levels1, stringsAsFactors=FALSE, row.names=NULL)
merge(df, df2, by.x="weather", by.y="wxfrom", all.x=TRUE)
#      weather  wxto
# 1      Clear   dry
# 2      Clear   dry
# 3 Hail/sleet   wet
# 4      Other  <NA>
# 5       Rain   wet
# 6       Rain   wet
# 7       Snow   wet
# 8    Unknown other

类似于:

dplyr::left_join(df, df2, by=c("weather"="wxfrom"))

使用默认值查找

transform(df, newwx = levels1[ match(as.character(weather), names(levels1), nomatch=1L) ])
#      weather newwx
# 1      Clear   dry
# 2       Snow   wet
# 3      Clear   dry
# 4       Rain   wet
# 5       Rain   wet
# 6      Other other
# 7 Hail/sleet   wet
# 8    Unknown other

这最后一个具有天生的能力,可以为任何不匹配项分配未知数。与其他方法一样,它就像做ifelse(is.na(newwx), "unk", newwx)一样简单,因此不会增加很多。

答案 3 :(得分:0)

代码:

sapply(df$weather, function(w) names(weather.levels[sapply(lapply(weather.levels, function(y) lapply(y, function(x) w %in% x)), function(z) any(z))]))

然后将其绑定到df并更改col名称。

答案 4 :(得分:0)

请注意,已接受答案中的两个解决方案都不再适用于 R-4.0.2。 new.weather 返回与 weather 列相同的值。

df <- data.frame(weather = c('Clear','Snow','Clear','Rain','Rain','Other','Hail/sleet','Unknown'))
weather.levels <- list(
  dry = c('Clear', 'Cloudy'),
  wet = c('Snow', 'Rain', 'Hail/sleet'),
  other = c('Other','Unknown'))
df$new.weather <- `levels<-`(df$weather, weather.levels)

Result in R-4.0.2

相同的代码段适用于 R-3.5.2。

Result in R-3.5.2