Подтвердить что ты не робот

Почему as.matrix добавляет лишние пробелы при преобразовании числа в символ?

Если вы используете применение по строкам в data.frame с символьными и числовыми столбцами, применяйте as.matrix внутри, чтобы преобразовать data.frame только в символы. Но если числовой столбец состоит из чисел разной длины, as.matrix добавляет пробелы, чтобы соответствовать наивысшему/ "самому длинному" числу.

Пример:

df <- data.frame(id1=c(rep("a",3)),id2=c(100,90,8), stringsAsFactors = FALSE) 
df
##   id1 id2
## 1   a 100
## 2   a  90
## 3   a   8
as.matrix(df)
##      id1 id2  
## [1,] "a" "100"
## [2,] "a" " 90"
## [3,] "a" "  8"

Я ожидал, что результатом будет:

     id1 id2  
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"

Почему дополнительные пробелы?

Они могут создавать неожиданные результаты при использовании apply на data.frame:

myfunc <- function(row){
  paste(row[1], row[2], sep = "")
}
> apply(df, 1, myfunc)
[1] "a100" "a 90" "a  8"
> 

В то время как цикл дает ожидаемый результат.

> for (i in 1:nrow(df)){
  print(myfunc(df[i,]))
}
[1] "a100"
[1] "a90"
[1] "a8"

и

> paste(df[,1], df[,2], sep = "")
[1] "a100" "a90"  "a8"  

Есть ли ситуации, когда дополнительные пробелы, добавленные с помощью as.matrix, полезны?

4b9b3361

Ответ 1

Это связано с тем, что нецифровые данные преобразуются в метод as.matrix.data.frame. Существует простой подход, показанный ниже.

Подробнее

?as.matrix отмечает, что преобразование выполняется через format(), и именно здесь добавляются дополнительные пробелы. В частности, ?as.matrix имеет это в разделе Подробности:

 ‘as.matrix’ is a generic function.  The method for data frames
 will return a character matrix if there is only atomic columns and
 any non-(numeric/logical/complex) column, applying ‘as.vector’ to
 factors and ‘format’ to other non-character columns.  Otherwise,
 the usual coercion hierarchy (logical < integer < double <
 complex) will be used, e.g., all-logical data frames will be
 coerced to a logical matrix, mixed logical-integer will give a
 integer matrix, etc.

?format также отмечает, что

Символьные строки заполняются пробелами до ширины отображения самого широкого.

Рассмотрим этот пример, иллюстрирующий поведение

> format(df[,2])
[1] "100" " 90" "  8"
> nchar(format(df[,2]))
[1] 3 3 3

format не должен работать таким образом, как он имеет trim:

trim: logical; if ‘FALSE’, logical, numeric and complex values are
      right-justified to a common width: if ‘TRUE’ the leading
      blanks for justification are suppressed.

например.

> format(df[,2], trim = TRUE)
[1] "100" "90"  "8"

но невозможно передать этот аргумент методу as.matrix.data.frame.

Обход

Способ обойти это - применить format() самостоятельно, вручную, через sapply. Там вы можете перейти в trim = TRUE

> sapply(df, format, trim = TRUE)
     id1 id2  
[1,] "a" "100"
[2,] "a" "90" 
[3,] "a" "8"

или, используя vapply, мы можем указать, что мы ожидаем вернуть (здесь символьные векторы длины 3 [nrow(df)]):

> vapply(df, format, FUN.VALUE = character(nrow(df)), trim = TRUE)
     id1 id2  
[1,] "a" "100"
[2,] "a" "90" 
[3,] "a" "8"

Ответ 2

Кажется немного странным. В руководстве (?as.matrix) объясняется, что format вызывается для преобразования в матрицу символов:

Метод для фреймов данных возвращает матрицу символов, если есть только атомные столбцы и любой столбцы (числовые/логические/сложные) применение as.vector к факторам и формат для других несимвольных столбцы.

И вы можете видеть, что если вы вызываете format напрямую, он делает то, что as.matrix делает:

format(df$id2)
[1] "100" " 90" "  8"

Что вам нужно сделать, это передать trim arugment:

format(df$id2,trim=TRUE)
[1] "100" "90"  "8" 

Но, к сожалению, функция as.matrix.data.frame не позволяет вам это делать.

else if (non.numeric) {
    for (j in pseq) {
        if (is.character(X[[j]])) 
            next
        xj <- X[[j]]
        miss <- is.na(xj)
        xj <- if (length(levels(xj))) 
            as.vector(xj)
        else format(xj) # This could have ... as an argument
        # else format(xj,...)
        is.na(xj) <- miss
        X[[j]] <- xj
    }
}

Итак, вы можете изменить as.data.frame.matrix. Я думаю, что это было бы приятным дополнением, однако, чтобы включить это в базу.

Но быстрым решением было бы просто:

as.matrix(data.frame(lapply(df,as.character)))
     id1 id2  
[1,] "a" "100"
[2,] "a" "90" 
[3,] "a" "8"  
# As mentioned in the comments, this also works:
sapply(df,as.character)

Ответ 3

as.matrix вызывает format внутренне:

 > format(df$id2)
[1] "100" " 90" "  8"

То, откуда происходят дополнительные пробелы. format имеет дополнительный аргумент trim, чтобы удалить те:

> format(df$id2, trim = TRUE)
[1] "100" "90"  "8"  

Однако вы не можете предоставить этот аргумент as.matrix.

Ответ 4

Причина такого поведения уже объясняется в предыдущих ответах, но я хотел бы предложить другой способ обхода этого:

df <- data.frame(id1=c(rep("a",3)),id2=c(100,90,8), stringsAsFactors = FALSE) 
do.call(cbind,df)
     id1 id2  
[1,] "a" "100"
[2,] "a" "90" 
[3,] "a" "8"  

Обратите внимание, что при использовании stringsAsFactors = TRUE это не работает, поскольку уровни факторов преобразуются в числа.