Преобразование типа нескольких столбцов в кадре данных одновременно - PullRequest
36 голосов
/ 07 октября 2011

Кажется, я трачу много времени на создание кадра данных из файла, базы данных или чего-то еще, а затем преобразовываю каждый столбец в нужный мне тип (числовой, коэффициент, символ и т. Д.).Есть ли способ сделать это за один шаг, возможно, указав вектор типов?

foo<-data.frame(x=c(1:10), 
                y=c("red", "red", "red", "blue", "blue", 
                    "blue", "yellow", "yellow", "yellow", 
                    "green"),
                z=Sys.Date()+c(1:10))

foo$x<-as.character(foo$x)
foo$y<-as.character(foo$y)
foo$z<-as.numeric(foo$z)

вместо трех последних команд, я хотел бы сделать что-то вроде

foo<-convert.magic(foo, c(character, character, numeric))

Ответы [ 8 ]

32 голосов
/ 07 октября 2011

Редактировать См. этот связанный вопрос для некоторых упрощений и расширений этой основной идеи.

Мой комментарий к ответу Брэндона, используя switch:

convert.magic <- function(obj,types){
    for (i in 1:length(obj)){
        FUN <- switch(types[i],character = as.character, 
                                   numeric = as.numeric, 
                                   factor = as.factor)
        obj[,i] <- FUN(obj[,i])
    }
    obj
}

out <- convert.magic(foo,c('character','character','numeric'))
> str(out)
'data.frame':   10 obs. of  3 variables:
 $ x: chr  "1" "2" "3" "4" ...
 $ y: chr  "red" "red" "red" "blue" ...
 $ z: num  15254 15255 15256 15257 15258 ...

Для действительно больших фреймов данных вы можете использовать lapply вместо цикла for:

convert.magic1 <- function(obj,types){
    out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
    names(out) <- colnames(obj)
    as.data.frame(out,stringsAsFactors = FALSE)
}

При этом помните о некоторых тонкостях принудительного приведения данных в R. Например, преобразование из фактора в число часто включает as.numeric(as.character(...)). Также следует помнить о поведении по умолчанию data.frame() и as.data.frame() s при преобразовании символа в фактор.

17 голосов
/ 13 июля 2015

Если вы хотите автоматически определять тип данных столбцов, а не указывать его вручную (например, после очистки данных и т. Д.), Может помочь функция type.convert().

Функция type.convert() принимает символьный вектор и пытается определить оптимальный тип для всех элементов (это означает, что он должен применяться один раз для каждого столбца).

df[] <- lapply(df, function(x) type.convert(as.character(x)))

Так как я люблю dplyr, я предпочитаю:

library(dplyr)
df <- df %>% mutate_all(funs(type.convert(as.character(.))))
7 голосов
/ 07 октября 2011

Я также часто сталкиваюсь с этим. Это о том, как вы импортируете данные. Все функции read ... () имеют опцию определенного типа, чтобы указывать не преобразовывать символьные строки в фактор. Это означает, что текстовые строки будут оставаться символами, а вещи, которые выглядят как числа, будут оставаться числами. Проблема возникает, когда у вас есть пустые элементы, а не NA. Но опять же, na.strings = c ("", ...) также должны решить эту проблему. Я бы начал с тщательного изучения процесса импорта и соответствующей корректировки.

Но вы всегда можете создать функцию и протолкнуть эту строку.

convert.magic <- function(x, y=NA) {
for(i in 1:length(y)) { 
if (y[i] == "numeric") { 
x[i] <- as.numeric(x[[i]])
}
if (y[i] == "character")
x[i] <- as.character(x[[i]])
}
return(x)
}

foo <- convert.magic(foo, c("character", "character", "numeric"))

> str(foo)
'data.frame':   10 obs. of  3 variables:
 $ x: chr  "1" "2" "3" "4" ...
 $ y: chr  "red" "red" "red" "blue" ...
 $ z: num  15254 15255 15256 15257 15258 ...
5 голосов
/ 01 октября 2014

Я знаю, что уже поздно отвечать, но использование цикла вместе с функцией атрибутов - простое решение вашей проблемы.

names <- c("x", "y", "z")
chclass <- c("character", "character", "numeric")

for (i in (1:length(names))) {
  attributes(foo[, names[i]])$class <- chclass[i]
}
2 голосов
/ 17 мая 2014

Я только что натолкнулся на метод извлечения RSQLite ... результаты возвращаются как атомарные типы данных.В моем случае это была отметка даты и времени, которая вызывала у меня разочарование.Я обнаружил, что функция setAs очень полезна, чтобы заставить as работать как положено.Вот мой маленький пример.

##data.frame conversion function
convert.magic2 <- function(df,classes){
  out <- lapply(1:length(classes),
                FUN = function(classIndex){as(df[,classIndex],classes[classIndex])})
  names(out) <- colnames(df)
  return(data.frame(out))
}

##small example case
tmp.df <- data.frame('dt'=c("2013-09-02 09:35:06", "2013-09-02 09:38:24", "2013-09-02 09:38:42", "2013-09-02 09:38:42"),
                     'v'=c('1','2','3','4'),
                     stringsAsFactors=FALSE)
classes=c('POSIXct','numeric')
str(tmp.df)
#confirm that it has character datatype columns
##  'data.frame':  4 obs. of  2 variables:
##    $ dt: chr  "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
##    $ v : chr  "1" "2" "3" "4"

##is the dt column coerceable to POSIXct?
canCoerce(tmp.df$dt,"POSIXct")
##  [1] FALSE

##and the conver.magic2 function fails also:
tmp.df.n <- convert.magic2(tmp.df,classes)

##  Error in as(df[, classIndex], classes[classIndex]) : 
##    no method or default for coercing “character” to “POSIXct” 

##ittle reading reveals the setAS function
setAs('character', 'POSIXct', function(from){return(as.POSIXct(from))})

##better answer for canCoerce
canCoerce(tmp.df$dt,"POSIXct")
##  [1] TRUE

##better answer from conver.magic2
tmp.df.n <- convert.magic2(tmp.df,classes)

##column datatypes converted as I would like them!
str(tmp.df.n)

##  'data.frame':  4 obs. of  2 variables:
##    $ dt: POSIXct, format: "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
##   $ v : num  1 2 3 4
1 голос
/ 05 июня 2016

Несколько простое решение для data.table, хотя потребуется несколько шагов, если вы переходите на множество различных типов столбцов.

dt <- data.table( x=c(1:10), y=c(10:20), z=c(10:20), name=letters[1:10])

dt <- dt[, lapply(.SD, as.numeric), by= name]

Это изменит все столбцы, кроме тех, которые указаны в by, на числовые (или все, что вы установили в lapply)

0 голосов
/ 19 декабря 2017

Преобразование - это то, что вы описываете:

foo <- transform(foo, x=as.character(x), y=as.character(y), z=as.numeric(z))
0 голосов
/ 07 декабря 2015

Добавление к ответу @ joran, в котором convert.magic не будет сохранять числовые значения при преобразовании коэффициента в цифру:

convert.magic <- function(obj,types){
    out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
    character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
    names(out) <- colnames(obj)
    as.data.frame(out,stringsAsFactors = FALSE)
}

foo<-data.frame(x=c(1:10), 
                    y=c("red", "red", "red", "blue", "blue", 
                        "blue", "yellow", "yellow", "yellow", 
                        "green"),
                    z=Sys.Date()+c(1:10))

foo$x<-as.character(foo$x)
foo$y<-as.character(foo$y)
foo$z<-as.numeric(foo$z)

str(foo)
# 'data.frame': 10 obs. of  3 variables:
# $ x: chr  "1" "2" "3" "4" ...
# $ y: chr  "red" "red" "red" "blue" ...
# $ z: num  16777 16778 16779 16780 16781 ...

foo.factors <- convert.magic(foo, rep("factor", 3))

str(foo.factors) # all factors

foo.numeric.not.preserved <- convert.magic(foo.factors, c("numeric", "character", "numeric"))

str(foo.numeric.not.preserved)
# 'data.frame': 10 obs. of  3 variables:
# $ x: num  1 3 4 5 6 7 8 9 10 2
# $ y: chr  "red" "red" "red" "blue" ...
# $ z: num  1 2 3 4 5 6 7 8 9 10

# z comes out as 1 2 3...

Ниже следует сохранить числовые значения:

## as.numeric function that preserves numeric values when converting factor to numeric

as.numeric.mod <- function(x) {
    if(is.factor(x))
      as.numeric(levels(x))[x]
  else
      as.numeric(x)
}

## The same than in @joran's answer, except for as.numeric.mod
convert.magic <- function(obj,types){
    out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
    character = as.character,numeric = as.numeric.mod, factor = as.factor); FUN1(obj[,i])})
    names(out) <- colnames(obj)
    as.data.frame(out,stringsAsFactors = FALSE)
}

foo.numeric <- convert.magic(foo.factors, c("numeric", "character", "numeric"))

str(foo.numeric)
# 'data.frame': 10 obs. of  3 variables:
# $ x: num  1 2 3 4 5 6 7 8 9 10
# $ y: chr  "red" "red" "red" "blue" ...
# $ z: num  16777 16778 16779 16780 16781 ...

# z comes out with the correct numeric values
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...