我有一个数据框列,其中包含许多文本值(级别)。我需要将这些值映射到预定义的类似对象的结构,以减少级别数。我可以使用字典在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
我看过诸如this和this之类的解决方案,但是这些解决方案无法回答我的问题。我无法创建使用R的match
函数的数据框,因为预设字典weather.levels
的每个类别(“干”,“湿”,“其他”)中的级别数不同。
答案 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)
相同的代码段适用于 R-3.5.2。