R导入stata文件存在法语重音字符问题

问题描述 投票:0回答:1

我有一个很大的 stata 文件,我认为其中有一些法国口音的字符保存得不好。

当我导入编码设置为空白的文件时,它不会读入。当我将其设置为

latin1
时,它会读入,但在一个变量中,并且我确信在其他变量中,法语重音字符是未正确渲染。我对另一个 stata 文件也遇到了类似的问题,我尝试在这里应用修复(实际上在这种情况下不起作用,但似乎很重要)。

说实话,似乎是这里真正的问题。很多乱码是“实际的”,它们符合“预期的”,但我不知道回去。

可重现的代码在这里:


library(haven)
library(here)
library(tidyverse)
library(labelled)
#Download file
temp <- tempfile()
temp2 <- tempfile()

download.file("https://github.com/sjkiss/Occupation_Recode/raw/main/Data/CES-E-2019-online_F1.dta.zip", temp)
unzip(zipfile = temp, exdir = temp2)
ces19web <- read_dta(file.path(temp2, "CES-E-2019-online_F1.dta"), encoding="latin1")

#Try with encoding set to blank, it won't work. 
#ces19web <- read_dta(file.path(temp2, "CES-E-2019-online_F1.dta"), encoding="")

unlink(c(temp, temp2))

#### Diagnostic section for accented characters ####
ces19web$cps19_prov_id
#Note value labels are cut-off at accented characters in Quebec. 
#I know this occupation has messed up characters
ces19web %>% 
  filter(str_detect(pes19_occ_text,"assembleur-m")) %>% 
  select(cps19_ResponseId, pes19_occ_text)
#Check the encodings of the occupation titles and store in a variable encoding
ces19web$encoding<-Encoding(ces19web$pes19_occ_text)
#Check encoding of problematic characters
ces19web %>% 
  filter(str_detect(pes19_occ_text,"assembleur-m")) %>% 
  select(cps19_ResponseId, pes19_occ_text, encoding) 
#Write out messy occupation titles
ces19web %>% 
  filter(str_detect(pes19_occ_text,"Ã|©")) %>% 
  select(cps19_ResponseId, pes19_occ_text, encoding) %>% 
  write_csv(file=here("Data/messy.csv"))

#Try to fix

source("https://github.com/sjkiss/Occupation_Recode/raw/main/fix_encodings.R")
#store the messy variables in messy
messy<-ces19web$pes19_occ_text
library(stringi)
#Try to clean with the function fix_encodings
ces19web$pes19_occ_text_cleaned<-stri_replace_all_fixed(messy, names(fixes), fixes, vectorize_all = F)

#Examine
ces19web %>% 
  filter(str_detect(pes19_occ_text_cleaned,"Ã|©")) %>% 
  select(cps19_ResponseId, pes19_occ_text, pes19_occ_text_cleaned, encoding) %>% 
head()

r unicode encoding utf-8 r-haven
1个回答
1
投票

您的数据文件是 dta 版本 113 文件(文件中的第一个字节是 113)。也就是说,它是一个 Stata 8 文件,尤其是 Stata 14 之前的文件,因此使用自定义编码(Stata >=14 使用 UTF-8)。

因此使用

encoding
read_dta
参数似乎是正确的。但这里有一些问题,用十六进制编辑器可以看出。

首先,重音字母处的截断标签(例如 Québec → Qu)实际上不是由 Haven 引起的:它们在 dta 文件中存储为截断的。

pes19_occ_text
采用 UTF-8 编码,您可以通过以下方式进行检查:

ces19web <- read_dta("CES-E-2019-online_F1.dta", encoding="UTF-8")
grep("^Producteur", unique(ces19web$pes19_occ_text), value = T)

output: "Producteur télé"

这个

"é"
是读作latin1的UTF-8数据(这里是
"é"
)的特征。 但是,如果您尝试使用
encoding="UTF-8"
导入,
read_dta
将失败:文件中可能存在其他非 UTF-8 字符,
read_dta
无法读取为 UTF-8。导入后我们必须做一些事情。

这里,

read_dta
正在做一些令人讨厌的事情:它像latin1数据一样导入
"Producteur télé"
,并转换为UTF-8,因此编码字符串实际上具有UTF-8字符“Ë”和“©”。

要解决此问题,您必须首先转换回 latin1。该字符串仍将是“Producteur télé”,但以 latin1 编码。

然后,您只需将编码强制为 UTF-8,而不需要进行转换,而无需更改数据。

这是代码:

ces19web <- read_dta("CES-E-2019-online_F1.dta", encoding="")
ces19web$pes19_occ_text <- iconv(ces19web$pes19_occ_text, from = "UTF-8", to = "latin1")
Encoding(ces19web$pes19_occ_text) <- "UTF-8"
grep("^Producteur", unique(ces19web$pes19_occ_text), value = T)

output: "Producteur télé"

您可以对带有变音符号的其他变量执行相同的操作。


如果我们使用

iconv
转换为原始数据以查看实际字节,那么这里
charToRaw
的使用可能会更容易理解。导入数据后,“télé”就是“74 c3 83 c2 a9 6c c3 83 c2 a9”在UTF-8中的表示。第一个字节 0x74(十六进制)是字母“t”,0x6c 是字母“l”。在这之间,我们有四个字节,而不是 UTF-8 中的字母“é”只有两个字节(“c3 a9”,即读作 latin1 时的“é”)。

实际上,“c3 83”是“à”,“c2 a9”是“©”。

因此,我们首先要把这些字符转换回latin1,这样它们每个就占一个字节。那么“74 c3 a9 6c c3 a9”就是“télé”的编码,但这次是 latin1。也就是说,该字符串与 UTF-8 编码的“télé”具有相同的字节,我们只需要告诉 R 编码不是 latin1 而是 UTF-8(这不是转换)。 另请参阅

Encoding

iconv 的帮助页面。

现在一个好问题可能是:您最初是如何得到如此糟糕的 dta 文件的? Stata 8 文件保存 UTF-8 数据是相当令人惊讶的。

我想到的第一个想法是

saveold

命令的错误使用,该命令允许将数据保存在旧版本的 Stata 文件中。但根据参考手册,在Stata 14中saveold只能存储Stata >=11的文件。

也许第三方工具做到了这一点,以及标签的错误截断?例如,它可能是 SAS 或 SPSS。我不知道您的数据来自哪里,但公共提供商使用 SAS 进行内部工作并发布转换后的数据集的情况并不罕见。例如,来自“欧洲社会调查”的数据集以 SAS、SPSS 和 Stata 格式提供,但如果我没记错的话,最初只有 SAS 和 SPSS,后来出现了 Stata:Stata 文件可能只是使用其他工具转换的。 

回答评论:如何循环字符变量来执行相同的操作? dplyr 有一种更聪明的方法,但这里是一个带有基本 R 的简单循环。

ces19web <- read_dta("CES-E-2019-online_F1.dta") for (n in names(ces19web)) { v <- ces19web[[n]] if (is.character(v)) { v <- iconv(v, from = "UTF-8", to = "latin1") Encoding(v) <- "UTF-8" } ces19web[[n]] <- v }

© www.soinside.com 2019 - 2024. All rights reserved.