在R中将多年份列宽格式数据转长格式及UTF-8问题排查
宽格式转长格式解决方案(含编码与空格列处理)
我有一份宽格式数据,包含10个年份列、19类带年份后缀的连续数据列,以及ID和分类列,需要转换为单一年份列的长格式。尝试过plyr、reshape、melt等方法均未成功,同时遇到UTF-8编码问题,持续报错“Error unexpected symbol”。部分类别名称含空格(如“Sum eiendeler”),怀疑与plyr兼容性有关。以下是数据示例的dput子集:
dput(head(subset, encoding="UTF-8")) structure(list(Kommunenr = c(3024, 3003, 3026, 3024, 3005, 3024 ), Kommunenavn = c("B�RUM", "SARPSBORG", "AURSKOG-H�LAND", "B�RUM", "DRAMMEN", "B�RUM"), Poststed = c("LYSAKER", "SARPSBORG", "AURSKOG", "OSLO", "DRAMMEN", "LYSAKER"), Firmanavn = c("SIBELCO NORDIC AS", "BORREGAARD AS", "RENOR AS", "IBKA NORGE AS", "LINDUM AS", "ABB AS"), Omsetning = c(1120829, 4560381, 165102, 95528, 534988, 4831283 ), `Omsetning (-1 år)` = c(970373, 4109890, 164965, 95592, 590392, 4447881), `Omsetning (-2 år)` = c(1042016, 3833545, 152611, 134110, 571098, 4913517), `Omsetning (-3 år)` = c(1139725, 3632657, 156393, 126319, 503298, 8528459), `Omsetning (-4 år)` = c(1101891, 3494357, 139160, 127351, 501145, 8629261), `Omsetning (-5 år)` = c(1018331, 3354595, 128425, 328448, 467739, 9062316), `Omsetning (-6 år)` = c(967473, 3032051, 128607, 480547, 364653, 9281909), `Omsetning (-7 år)` = c(925051, 3001124, 137716, 587045, 321790, 11213573), `Omsetning (-8 år)` = c(817706, 3076555, 135565, 620426, 281758, 11363403), `Omsetning (-9 år)` = c(869521, 3001972, 143661, 506029, 258282, 9596080), `Lønnskostnader` = c(222248, 801995, 45854, 44275, 134000, 1638857), `Ordinært resultat` = c(75771, 625441, 4405, -4518, 20387, 210731), `Ordinært resultat (-1 år)` = c(102996, 382166, 4020, 1735, 47294, 34840), `Ordinært resultat (-2 år)` = c(129144, 375137, 3896, -4458, 30535, 134473), `Ordinært resultat (-3 år)` = c(89943, 468869, 5320, 24016, 35222, 304241), `Ordinært resultat (-4 år)` = c(153231, 509385, 714, -5844, 46260, 298099), `Ordinært resultat (-5 år)` = c(160850, 512248, -6328, 64602, 28472, 315443), `Ordinært resultat (-6 år)` = c(154387, 247849, -7225, -7176, 23329, 564357), `Ordinært resultat (-7 år)` = c(102746, 212834, -4144, -7117, 23103, 649626), `Ordinært resultat (-8 år)` = c(43573, 143042, -1720, 2221, 42606, 544287), `Ordinært resultat (-9 år)` = c(55495, 242609, 3700, -2504, 29300, 620666), Goodwill = c(0, 0, 0, 0, 0, 1124), `Goodwill (-1 år)` = c(0, 0, 0, 0, 0, 2188), `Goodwill (-2 år)` = c(0, 0, 0, 0, 0, 6963), `Goodwill (-3 år)` = c(131834, 0, 0, 0, 0, 12790), `Goodwill (-4 år)` = c(134854, 0, 0, 360, 0, 26327), `Goodwill (-5 år)` = c(138229, 0, 0, 0, 0, 39247), `Goodwill (-6 år)` = c(141427, 0, 0, 20613, 0, 47812), `Goodwill (-7 år)` = c(144031, 0, 0, 23027, 497, 0), `Goodwill (-8 år)` = c(146560, 0, 0, 25713, 0, 0), `Goodwill (-9 år)` = c(149051, 0, 0, 36596, 0, 77302), Orgnr = c(965724737, 895623032, 935906865, 952070533, 979618840, 982085160), `Regnskapsår` = c(2021, 2021, 2021, 2021, 2021, 2021), `Regnskapsår (-1 år)` = c(2020, 2020, 2020, 2020, 2020, 2020), `Regnskapsår (-2 år)` = c(2019, 2019, 2019, 2019, 2019, 2019), `Regnskapsår (-3 år)` = c(2018, 2018, 2018, 2018, 2018, 2018), `Regnskapsår (-4 år)` = c(2017, 2017, 2017, 2017, 2017, 2017), `Regnskapsår (-5 år)` = c(2016, 2016, 2016, 2016, 2016, 2016), `Regnskapsår (-6 år)` = c(2015, 2015, 2015, 2015, 2015, 2015), `Regnskapsår (-7 år)` = c(2014, 2014, 2014, 2014, 2014, 2014), `Regnskapsår (-8 år)` = c(2013, 2013, 2013, 2013, 2013, 2013), `Regnskapsår (-9 år)` = c(2012, 2012, 2012, 2012, 2012, 2012)), row.names = c(NA, -6L), class = c("tbl_df", "tbl", "data.frame"))
一、编码问题预处理
先解决UTF-8编码异常,确保字符列显示正常:
library(tidyverse) # 针对dput导入的数据,统一转换字符列为UTF-8编码 df <- structure(...) # 替换为你的dput内容 df <- df %>% mutate(across(where(is.character), ~iconv(., from = "latin1", to = "UTF-8")))
二、用pivot_longer实现宽转长
tidyverse的pivot_longer对含空格、特殊字符的列名兼容性更强,无需担心plyr的适配问题:
long_df <- df %>% pivot_longer( cols = -c(Kommunenr, Kommunenavn, Poststed, Firmanavn, Orgnr), # 保留不需要转换的ID/分类列 names_to = c("指标类型", "年份偏移"), # 正则匹配列名,拆分指标类型和年份后缀 names_pattern = "(.*?)(?: \\((-\\d+ år)\\))?$", values_to = "指标值" ) %>% # 计算实际年份 mutate( 年份偏移 = replace_na(年份偏移, "0 år"), 年份偏移数值 = as.integer(str_extract(年份偏移, "-?\\d+")), 实际年份 = `Regnskapsår` + 年份偏移数值 ) %>% # 整理列顺序,移除冗余列 select(Kommunenr, Kommunenavn, Poststed, Firmanavn, Orgnr, 实际年份, 指标类型, 指标值)
正则说明
(.*?)(?: \\((-\\d+ år)\\))?$:
(.*?):非贪婪匹配指标名称(如Omsetning、Ordinært resultat)(?: \\((-\\d+ år)\\))?$:可选匹配年份后缀(如(-1 år)),并提取偏移数值
三、验证结果
查看转换后的前几行数据:
head(long_df)
额外问题处理
如果仍有编码异常,用stringi包强制转换:
library(stringi) df <- df %>% mutate(across(where(is.character), stri_enc_toutf8))
内容的提问来源于stack exchange,提问作者SarpsRosing
相关产品推荐
相关产品推荐

