Предположим, у меня есть фрейм данных:
df <- data.frame(
x = c("Yes","No","Yes","Don't Know"),
y = c("Male","Female","Refused","Male"),
z = c("Employed", "Unemployed","Employed","Employed")
)
Я хочу перекодировать значения во всем кадре данных в числовые значения и сохранить коды в отдельном кадре данных:
codes <- data.frame(
variable = c(rep("x",3),rep("y",3),rep("z",2)),
labels = c("Yes","No","Don't Know","Male","Female","Refused","Employed", "Unemployed"),
codes = c(1,2,98,1,2,99,1,2)
)
Я хочу, чтобы вывод выглядел так:
> df
x y z
1 1 1 1
2 2 2 2
3 1 99 1
4 98 1 1
Как я могу подойти к этой проблеме? Решение tidyverse было бы очень полезно.





Можно было бы объединить данные в длинном формате с набором данных codes и переместить их обратно в широкий формат.
Используя tidyverse это можно сделать следующим образом:
library(dplyr)
library(tidyr)
df %>%
pivot_longer(cols = everything()) %>%
left_join(codes, join_by(name == variable, value == labels)) %>%
select(-value) %>%
pivot_wider(names_from = name, values_from = codes, values_fn = list) %>%
unnest_longer(everything())
# A tibble: 4 × 3
# x y z
# <dbl> <dbl> <dbl>
#1 1 1 1
#2 2 2 2
#3 1 99 1
#4 98 1 1
Прокрутите столбцы, найдите совпадения на метках, получите соответствующие коды:
sapply(names(df), function(i){
ix <- which(codes$variable == i)
codes[ ix, "codes" ][ match(df[[ i ]], codes[ ix, "labels"]) ]})
# x y z
# [1,] 1 1 1
# [2,] 2 2 2
# [3,] 1 99 1
# [4,] 98 1 1
Используйте функцию Map, как показано ниже:
df2 = df
df2[] = Map(\(h, k) k$codes[match(h, k$labels)], df, split(codes, ~variable))
x y z
1 1 1 1
2 2 2 2
3 1 99 1
4 98 1 1
В tidyverse вы можете попробовать
df %>%
mutate(across(everything(), ~{with(
filter(codes, variable == cur_column()),
codes[match(.x, labels)]) }))
который дает
x y z
1 1 1 1
2 2 2 2
3 1 99 1
4 98 1 1
Еще одно простое решение с использованием именованного вектора:
a <- codes$codes
names(a) <- codes$labels
apply(df, 2, \(x) a[x])
x y z
[1,] 1 1 1
[2,] 2 2 2
[3,] 1 99 1
[4,] 98 1 1
Более надежным (и многословным) решением будет:
v <- tapply(codes$codes, codes$variable, c)
n <- tapply(codes$labels, codes$variable, c)
as.data.frame(Map(\(u, v, w) {a<-u;names(a)<-v;unname(a[w])}, v, n, df))
library(purrr)
library(dplyr)
lookup <- map(split(codes, ~ variable), \(dat) pull(dat, codes, labels))
df |>
mutate(across(everything(), \(x) lookup[[cur_column()]][x]))
# x y z
# 1 1 1 1
# 2 2 2 2
# 3 1 99 1
# 4 98 1 1
pivotвашdfв длинном формате, а затемleft_joinс помощьюcodes. При необходимостиpivotвернёмся к широкому формату.