Конвертировать сразу несколько столбцов кадра данных
Кажется, я потратил много времени на создание фрейма данных из файла, базы данных или чего-то еще, а затем преобразование каждого столбца в тип, в котором я хотел (число, коэффициент, символ и т.д.). Есть ли способ сделать это за один шаг, возможно, указав вектор типов?
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))
Ответы
Ответ 1
Изменить См. этот связанный вопрос для некоторых упрощений и расширений этой основной идеи.
Мой комментарий к Брэндону, используя 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()
поведение по умолчанию преобразования символа в коэффициент.
Ответ 2
Если вы хотите автоматически определять тип данных столбцов, а не вручную указывать его (например, после обработки данных и т.д.), Может помочь функция 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(.))))
Ответ 3
Я нахожу, что сталкиваюсь с этим много. Речь идет о том, как вы импортируете данные. Все функции 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 ...
Ответ 4
Я знаю, что уже поздно отвечаю, но использование цикла вместе с функцией атрибутов - простое решение вашей проблемы.
names <- c("x", "y", "z")
chclass <- c("character", "character", "numeric")
for (i in (1:length(names))) {
attributes(foo[, names[i]])$class <- chclass[i]
}
Ответ 5
Я просто столкнулся с чем-то подобным с методом выборки 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
Ответ 6
Дополнение к ответу @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 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
Ответ 7
Несколько простое решение 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
)
Ответ 8
Подобно type.convert(foo, as.is = TRUE)
, есть также readr::type_convert
, который преобразует фрейм данных в соответствующий класс, не указывая их
readr::type_convert(foo)
Если вы оставите все столбцы символами, мы также можем использовать readr::parse_guess
, который автоматически преобразует фрейм данных в правильные классы. Рассмотрим этот измененный фрейм данных
foo <- data.frame(x = as.character(1:10),
y = c("red", "red", "red", "blue", "blue", "blue", "yellow",
"yellow", "yellow", "green"),
z = as.character(Sys.Date()+c(1:10)), stringsAsFactors = FALSE)
str(foo)
#'data.frame': 10 obs. of 3 variables:
# $ x: chr "1" "2" "3" "4" ...
# $ y: chr "red" "red" "red" "blue" ...
# $ z: chr "2019-08-12" "2019-08-13" "2019-08-14" "2019-08-15" ...
Применение parse_guess
к каждому столбцу
foo[] <- lapply(foo, readr::parse_guess)
#'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: Date, format: "2019-08-12" "2019-08-13" "2019-08-14" "2019-08-15" ...
Ответ 9
Трансформация - это то, что вы, кажется, описываете:
foo <- transform(foo, x=as.character(x), y=as.character(y), z=as.numeric(z))
Ответ 10
Используя purrr
и base
:
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))
types <- c("character", "character", "numeric")
types<-paste0("as.",types)
purrr::map2_df(foo,types,function(x,y) do.call(y,list(x)))
# A tibble: 10 x 3
x y z
<chr> <chr> <dbl>
1 1 red 18127
2 2 red 18128
3 3 red 18129
4 4 blue 18130
Ответ 11
В пакете есть простое решение hablar
Код
library(hablar)
library(dplyr)
df <- data.frame(x = "1", y = "2", z = "4")
df %>%
convert(int(x, z),
chr(y))
Результат
# A tibble: 1 x 3
x y z
<int> <chr> <int>
1 1 2 4
Вы можете просто поместить несколько имен столбцов для преобразования нескольких столбцов, например, z
и z
в целое число, как в примере выше.