从一长串变量中重新编码

时间:2013-08-23 12:19:09

标签: r

使用示例数据框:

df <- structure(list(KY27SCH1 = c(4, 4, 4, 4, NA, 5, 2, 4, 4, NA, 4, 
                                  5, 3, 5, 5), KY27SCH2 = c(5, 4, 4, 4, NA, 4, 1, 4, 4, NA, 4, 
                                                            5, 4, 5, 5), KY27SCH3 = c(4, 4, 5, 4, NA, 4, 4, 4, 5, NA, 5, 
                                                                                      5, 3, 5, 5), KY27SCH4 = c(3, 5, 5, 4, NA, 5, 4, 5, 5, NA, 5, 
                                                                                                                5, 4, 5, 5)), .Names = c("KY27SCH1", "KY27SCH2", "KY27SCH3", 
                                                                                                                                         "KY27SCH4"), row.names = 197:211, class = "data.frame")

在将新列绑定到原始数​​据帧之前,我应用一行代码将四个不同的列添加到一起:

KC27sc_R <- rowSums(df[, c("KY27SCH1", "KY27SCH2", "KY27SCH3", "KY27SCH4")], na.rm = TRUE)
df <- cbind(df, KC27sc_R) # Adds columns to survey dataframe

然后,我想使用下面详述的结果表重新编码变量KC27sc_R:

5= -4.287
6 = -3.040
7 = -2.405
8 = -1.960
9 = -1.605
10 = -1.296
11 = -1.011
12 = -0.735
13 = -0.456
14 = -0.168
15 = 0.134
16 = 0.454
17 = 0.796
18 = 1.166
19 = 1.574
20 = 2.035
21 = 2.582
22 = 3.299 
23 = 4.594

即。列KC27sc_R中的5将变为-4.287。

有没有办法从数字列表中重新编码列而无需依次遍历每个数字?我通常使用重新编码功能,但我不确定如何使用大型列表执行此操作。

任何帮助将不胜感激。

7 个答案:

答案 0 :(得分:7)

假设我们已将您的查找表设为data.frame,如下所示:

mydf <- structure(list(V1 = c(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
    16, 17, 18, 19, 20, 21, 22, 23), V2 = c(-4.287, -3.04, -2.405, 
    -1.96, -1.605, -1.296, -1.011, -0.735, -0.456, -0.168, 0.134, 
    0.454, 0.796, 1.166, 1.574, 2.035, 2.582, 3.299, 4.594)), .Names = c("V1", 
    "V2"), class = "data.frame", row.names = c(NA, -19L))
mydf
#    V1     V2
# 1   5 -4.287
# 2   6 -3.040
# 3   7 -2.405
# 4   8 -1.960
# 5   9 -1.605
# 6  10 -1.296
# 7  11 -1.011
# 8  12 -0.735
# 9  13 -0.456
# 10 14 -0.168
# 11 15  0.134
# 12 16  0.454
# 13 17  0.796
# 14 18  1.166
# 15 19  1.574
# 16 20  2.035
# 17 21  2.582
# 18 22  3.299
# 19 23  4.594

你应该能够使用以下内容来获得你想要的东西:

df$RECODED <- mydf$V2[match(as.character(df$KC27sc_R), as.character(mydf$V1))]
df
#     KY27SCH1 KY27SCH2 KY27SCH3 KY27SCH4 KC27sc_R RECODED
# 197        4        5        4        3       16   0.454
# 198        4        4        4        5       17   0.796
# 199        4        4        5        5       18   1.166
# 200        4        4        4        4       16   0.454
# 201       NA       NA       NA       NA        0      NA
# 202        5        4        4        5       18   1.166
# 203        2        1        4        4       11  -1.011
# 204        4        4        4        5       17   0.796
# 205        4        4        5        5       18   1.166
# 206       NA       NA       NA       NA        0      NA
# 207        4        4        5        5       18   1.166
# 208        5        5        5        5       20   2.035
# 209        3        4        3        4       14  -0.168
# 210        5        5        5        5       20   2.035
# 211        5        5        5        5       20   2.035

其中的as.character部分有助于缓解潜在的FP问题。


这在概念上与已提供的merge解决方案非常相似,但可能会更快。

对人工数据集进行基准测试:

set.seed(1)
df <- data.frame(matrix(sample(0:25, 100000, replace = TRUE), ncol = 2))

library(microbenchmark)
microbenchmark(
  A = {
    df2 <- merge(df, mydf, by.x="X1", by.y="V1", sort = FALSE)
  },
  B = {
    df3 <- cbind(df, recoded = mydf$V2[match(as.character(df$X1), 
                                             as.character(mydf$V1))])
  }
)
Unit: milliseconds
#  expr       min        lq    median       uq       max neval
#     A 141.32530 149.61354 154.99230 162.7845 239.26242   100
#     B  24.93267  25.32541  25.73723  26.0792  96.44209   100

基本匹配方法的速度是merge的5倍。此外,merge往往会对您的行排序做一些时髦的事情。将原始数据集(df)的前几行与合并后的一行(df2)和我的解决方案(df3)进行比较。如您所见,merge完全重新排列data.frame,即使我们已指定“sort = FALSE”。

head(df)
#   X1 X2
# 1  6 15
# 2  9 18
# 3 14  8
# 4 23  3
# 5  5 22
# 6 23  1
head(df2)
#   X1 X2    V2
# 1  6 15 -3.04
# 2  6 23 -3.04
# 3  6  3 -3.04
# 4  6  0 -3.04
# 5  6 20 -3.04
# 6  6 16 -3.04
head(df3)
#   X1 X2 recoded
# 1  6 15  -3.040
# 2  9 18  -1.605
# 3 14  8  -0.168
# 4 23  3   4.594
# 5  5 22  -4.287
# 6 23  1   4.594

答案 1 :(得分:3)

如果您将重新编码值保留在第二个数据框中,则可以尝试merge

# original data
df <- data.frame(x = sample(5:7, 10, replace = TRUE))

# recoding data
df2 <- data.frame(x = 5:7, new_x = c(-4.287, -3.040, -2.405))

merge(df, df2)

编辑以下@ hadley的评论

上面的解决方案是测试浮点数是否相等,这不是一个可靠的方法,请参阅R FAQ 7.31 Why doesn't R think these numbers are equal?。处理此问题的一种方法是将用于合并的列设置为类“integer”。 (我注意到?merge

中的最后一个例子没有考虑这个潜在的问题
# original data
df <- data.frame(x = as.integer(sample(5:7, 10, replace = TRUE)))

# recoding data
df2 <- data.frame(x = as.integer(5:7), new_x = c(-4.287, -3.040, -2.405))

merge(df, df2)

编辑以下@Ananda Mahto的评论 - 合并和处理NA的时髦排序

# original data with NA
df <- data.frame(x = as.integer(c(7, NA, 5, 6, NA, 5)))

# recoding data as above, without NA
merge(df, df2, sort = FALSE)
# 'unspecified' order and no match with NA

# can at least handle NA by including NA also in recoding data
df2 <- data.frame(x = as.integer(c(5:7, NA)), new_x = c(-4.287, -3.040, -2.405, NA))
merge(df, df2, sort = FALSE)

可能的合并解决方案:join包中的plyr
    “与merge不同,join 保留x的顺序,无论使用何种连接类型”,并且在重新编码数据时不需要NA。

library(plyr)
df <- data.frame(x = as.integer(c(7, NA, 5, 6, NA, 5)))
df2 <- data.frame(x = as.integer(c(5:7)), new_x = c(-4.287, -3.040, -2.405))
join(df, df2)
# looks OK

来自?join:“Join通常比merge更快”。这是否是这种情况,以及它是否比match更快,我会留给其他人展示。

答案 2 :(得分:1)

我百分百肯定我的问题是对的。但我想你的问题是:你有一个从整数到值的映射,你想要用映射中指定的值替换数据帧(或向量)中的所有整数。

我会把映射放在一个列表中:

code = list()
code[[5 ]] = -4.287
code[[6 ]] = -3.040
code[[7 ]] = -2.405
code[[8 ]] = -1.960
code[[9 ]] = -1.605
code[[10]] = -1.296
code[[11]] = -1.011
code[[12]] = -0.735
code[[13]] = -0.456
code[[14]] = -0.168
code[[15]] = 0.134
code[[16]] = 0.454
code[[17]] = 0.796
code[[18]] = 1.166
code[[19]] = 1.574
code[[20]] = 2.035
code[[21]] = 2.582
code[[22]] = 3.299 
code[[23]] = 4.594

然后使用apply(或sapply for vector)进行替换:

apply(df, c(1,2), function(x) code[[x]])

答案 3 :(得分:1)

我喜欢@Henrik的merge解决方案,它似乎清晰易用。

我采用factor的方式,虽然我不认为转换回数字的过程非常优雅。使用cut的@ hadley解决方案类似。

df = data.frame(x = sample(5:7, 10, replace = TRUE))
# Using factor(), to convert to numeric have to go through a character
df$y = as.numeric(as.character(factor(df$x, labels = c(-4.287, -3.040, -2.405))))

# Using cut() is similar to factor, need to use the breaks argument
df$z = as.numeric(as.character(cut(df$x, breaks = 3, labels = c(-4.287, -3.040, -2.405))))

答案 4 :(得分:1)

答案需要最少的打字并且便携:

# Your original values
origval = seq(5,23)
newval = c(-4.287, -3.04, -2.405, -1.96, -1.605, -1.296, -1.011, -0.735, -0.456, -0.168, 0.134, 0.454, 0.796, 1.166, 1.574, 2.035, 2.582, 3.299, 4.594)

# generate a relationship
sp = smooth.spline(origval,newval)

# look up a value based on your original sequence
pval = predict(sp, origval)

现在pval$y将包含预测(转换)点。

您可以将任何其他值集合放入predict函数,代替origval,任何顺序,甚至是那些不在数据系列中的值(5.5等)

应用于您的数据集,您可以为变量创建占位符,然后“预测”其值:

df$KY_Rnew = df$KC27sc_R
df$KY_Rnew[!is.na(df$KY_Rnew)] = predict(sp,df$KY_Rnew[!is.na(df$KY_Rnew)])$y

答案 5 :(得分:1)

假设您的映射值都是整数,您可以在映射值的位置创建一个包含编码值的向量:

# using mydf defined by Ananda Mahto:
mydf <- structure(list(V1 = c(5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
 16, 17, 18, 19, 20, 21, 22, 23), V2 = c(-4.287, -3.04, -2.405, 
 -1.96, -1.605, -1.296, -1.011, -0.735, -0.456, -0.168, 0.134, 
 0.454, 0.796, 1.166, 1.574, 2.035, 2.582, 3.299, 4.594)), .Names = c("V1", 
 "V2"), class = "data.frame", row.names = c(NA, -19L))

# create vector with index positions corresponding to objective values:
vmap <- rep(NA, length=max(mydf$V1)) 
vmap[mydf$V1] <- mydf$V2

vmap
# [1]     NA     NA     NA     NA -4.287 -3.040 -2.405 -1.960 -1.605 -1.296
# [11] -1.011 -0.735 -0.456 -0.168  0.134  0.454  0.796  1.166  1.574  2.035
# [21]  2.582  3.299  4.594

# Assign NA to zero values in KC27sc_R (as you cannot have a zero position in a R vector)
# (this could also be another value defined in mydf if you want zero to map to something)
KC27sc_R[KC27sc_R==0] <- NA

# Then, select the values in vmap using the indices defined in KC27sc_R:
Krecode <- vmap[KC27sc_R]
data.frame(KC27sc_R, Krecode)


# KC27sc_R Krecode
# 197       16   0.454
# 198       17   0.796
# 199       18   1.166
# 200       16   0.454
# 201       NA      NA
# 202       18   1.166
# ... etc

由于所有操作都是矢量化的,因此长列表应该相当快。

答案 6 :(得分:1)

对于初学者,我们假设您的结果表存储在矩阵yo中:

yo <- matrix(0, nrow = 19, ncol = 2)
yo[, 1] <- c(5:23)
yo[, 2] <- c( -4.287, -3.040, -2.405, -1.960, -1.605, -1.296, -1.011, -0.735, -0.456, -0.168, 0.134, 0.454, 0.796, 1.166, 1.574, 2.035, 2.582, 3.299, 4.594)

即,yo的第一列对应于您想要更改的值,第二列 - 您要更改的内容。简而言之,将yo视为一个函数 - 第一列是此函数的 x 变量,第二列是函数的输出。

您需要弄清楚的第一件事是KC27sc_R的值的索引实际存在于yo[, 1]中(在这些行中,您实际上可以用新值替换旧值)。这样做是这样的:

ind <- which( df$KC27sc_R %in% yo[,1] )

ind为您提供可以更改的KC27sc_R的所有值的行。下一步是获得所有这些值:

a <- df[ind,]$KC27sc_R

最后一步是将a中的值与yo[, 1]中的值相关联 - 从字面上找到yo[, 1]行,您可以在a找到每个相应的值 - 函数match在这里会有所帮助:

b <- match( a, yo[,1] )

ind一样,b是一个索引 - 对于a中的每个值,它会告诉您需要去哪一行yo[, 2]才能找到替代品a中的此值。最后一步是替换df中的值:

df[ind, "KC27sc_R"] <- yo[b, 2]

这样就可以了。