为什么unicode字符串上的as.factor()会为每个操作系统返回不同的结果?

时间:2016-09-06 01:42:27

标签: r unicode

为什么这段代码:as.factor(c("\U201C", '"3', "1", "2", "\U00B5"))会在每个操作系统上返回不同的因子级别排序?

在Linux上:

> as.factor(c("\U201C",'"3', "1", "2","\U00B5")) [1] " "3 1 2 µ Levels: µ " 1 2 "3

在Windows上:

> as.factor(c("\U201C",'"3', "1", "2","\U00B5")) [1] " "3 1 2 µ Levels: "3 " µ 1 2

在Mac OS上:

>as.factor(c("\U201C",'"3', "1", "2","\U00B5")) [1] " "3 1 2 µ Levels: "3 " 1 2 µ

我让一些学生提交了包含as.numeric(as.factor(dat$var))的RMardkown作业。现在认为这不是一种好的编码方式,但输出的不一致会导致很多混乱和浪费时间。

3 个答案:

答案 0 :(得分:4)

这不仅仅是Unicode而不仅仅是R;一般来说sort(就像在* nix命令sort中一样)可以是特定于语言环境的。需要在所有计算机上通过LC_COLLATE(根据@ alistaire的评论)设置"C"(可能是Sys.setlocale)来消除差异。

对我而言,在Windows(7)上:

sort(c("Abc", "abc", "_abc", "ABC"))
[1] "_abc" "abc"  "Abc"  "ABC" 

而在Linux上(Ubuntu 12.04 ......哇,我需要升级那台机器)我得到了

sort(c("Abc", "abc", "_abc", "ABC"))
[1] "abc"  "_abc" "Abc"  "ABC" 

通过

按上述方式设置区域设置
Sys.setlocale("LC_COLLATE", "C")

给出

sort(c("Abc", "abc", "_abc", "ABC"))
[1] "ABC"  "Abc"  "_abc" "abc" 
两台机器上的

相同。

man的* nix sort页面提供了粗体警告

   *** WARNING *** The locale specified by the  environment  affects  sort
   order.  Set LC_ALL=C to get the traditional sort order that uses native
   byte values.

更新:看起来我在包含Unicode字符时重现了这个问题。问题追溯到sort - 尝试在示例中对向量进行排序。我似乎无法将区域设置(LC_COLLATELC_CTYPE)更改为"en_AU.UTF-8",这可能是一种潜在的解决方案。

答案 1 :(得分:2)

'因素'结构期望转换为字符值,因此需要以某种字体或其他字体编码。默认值是特定于操作系统的。词法排序顺序遵循语言环境。

在某种程度上@Roland先前对此问题的回答指出了语言环境问题,而不是编码问题:Is the default ("automatic") ordering for factors a part of the R specification? Alphabetical? Same on all platforms?

答案 2 :(得分:0)

我尝试更改区域设置,但无法解决此问题。但是,鉴于我们可以将此问题追溯到sort函数,一种可能的替代方法是在没有factor函数的情况下重新定义as.factorsort函数。

as.factor2 <- function(x){
  if (is.factor(x)) 
    x
  else if (!is.object(x) && is.integer(x)) {
    levels <- unique.default(x) # Removed sort()
    f <- match(x, levels)
    levels(f) <- as.character(levels)
    class(f) <- "factor"
    f
  }
  else factor2(x)
}

factor2 <- function (x = character(), levels, labels = levels, exclude = NA, 
          ordered = is.ordered(x), nmax = NA) 
{
  if (is.null(x)) 
    x <- character()
  nx <- names(x)
  if (missing(levels)) {
    y <- unique(x, nmax = nmax)
    ind <- 1:length(y) # Changed from sort.list(y)
    y <- as.character(y)
    levels <- unique(y[ind])
  }
  force(ordered)
  exclude <- as.vector(exclude, typeof(x))
  x <- as.character(x)
  levels <- levels[is.na(match(levels, exclude))]
  f <- match(x, levels)
  if (!is.null(nx)) 
    names(f) <- nx
  nl <- length(labels)
  nL <- length(levels)
  if (!any(nl == c(1L, nL))) 
    stop(gettextf("invalid 'labels'; length %d should be 1 or %d", 
                  nl, nL), domain = NA)
  levels(f) <- if (nl == nL) 
    as.character(labels)
  else paste0(labels, seq_along(levels))
  class(f) <- c(if (ordered) "ordered", "factor")
  f
}

我们现在可以按如下方式致电as.factor2

as.factor2(c("\U201C",'"3', "1", "2","\U00B5"))
# [1] “  "3 1  2  µ 
# Levels: "3 “ 1 2 µ

我不会说这是解决问题的方法;它更像是一种解决方法。特别是因为这涉及教学生,我宁愿不重新创建基础R功能。希望其他人可以提供更简单的解决方案。