Hoe converteer je een factor naar integer\numeriek zonder verlies van informatie?

Als ik een factor converteer naar een numeriek of geheel getal, krijg ik de onderliggende niveaucodes, niet de waarden als getallen.

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218
as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2
as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

Ik moet mijn toevlucht nemen tot pasteom de echte waarden te krijgen:

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

Is er een betere manier om een factor om te zetten in numeriek?


Antwoord 1, autoriteit 100%

Zie het gedeelte Waarschuwing van ?factor:

In het bijzonder as.numerictoegepast op
een factor is zinloos, en kan
gebeuren door impliciete dwang. Tot
transformeer een factor fnaar
ongeveer de oorspronkelijke numerieke
Waarden, as.numeric(levels(f))[f]is
aanbevolen en iets meer
efficiënt dan
as.numeric(as.character(f)).

De FAQ op R heeft vergelijkbaar advies .


Waarom is as.numeric(levels(f))[f]meer efficenter dan as.numeric(as.character(f))?

as.numeric(as.character(f))is effectief as.numeric(levels(f)[f]), dus u uitvoert de conversie naar Numeriek op length(x)waarden, in plaats van op nlevels(x)waarden. Het snelheidsverschil zal het duidelijkst zijn voor lange vectoren met weinig niveaus. Als de waarden meestal uniek zijn, zal er niet veel verschil zijn in snelheid. U doet echter de conversie, deze bewerking is onwaarschijnlijk dat deze het knelpunt in uw code is, dus maak je er dus geen zorgen te veel over.


enkele timings

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05

2, Autoriteit 13%

R heeft een aantal (ongedocumenteerde) gemaksfuncties voor het omrekenen van factoren:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor

Maar irritant genoeg is er niets om de factor -> numeriekeconversie. Als uitbreiding van het antwoord van Joshua Ulrich zou ik willen voorstellen om deze omissie te verhelpen met de definitie van uw eigen idiomatische functie:

as.numeric.factor <- function(x) {as.numeric(levels(x))[x]}

die u aan het begin van uw script kunt opslaan, of nog beter in uw .Rprofilebestand.


Antwoord 3, autoriteit 5%

De gemakkelijkste manier zou zijn om de functie unfactoruit pakket varhandledie een factorvectorof zelfs een dataframekan accepteren:

unfactor(your_factor_variable)

Dit voorbeeld kan een snelle start zijn:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)
class(x)  # -> "character"
class(y)  # -> "numeric"
x <- factor(x)
y <- factor(y)
class(x)  # -> "factor"
class(y)  # -> "factor"
library(varhandle)
x <- unfactor(x)
y <- unfactor(y)
class(x)  # -> "character"
class(y)  # -> "numeric"

Je kunt het ook op een dataframe gebruiken. Bijvoorbeeld de irisdataset:

sapply(iris, class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
   "numeric"    "numeric"    "numeric"    "numeric"     "factor"
# load the package
library("varhandle")
# pass the iris to unfactor
tmp_iris <- unfactor(iris)
# check the classes of the columns
sapply(tmp_iris, class)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
   "numeric"    "numeric"    "numeric"    "numeric"  "character"
# check if the last column is correctly converted
tmp_iris$Species
 [1] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
  [6] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [11] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [16] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [21] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [26] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"    
 [31] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [36] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [41] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [46] "setosa"     "setosa"     "setosa"     "setosa"     "setosa"
 [51] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [56] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [61] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [66] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [71] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [76] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [81] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [86] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [91] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
 [96] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
[101] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[106] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[111] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[116] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[121] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[126] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[131] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[136] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[141] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"
[146] "virginica"  "virginica"  "virginica"  "virginica"  "virginica"

Antwoord 4, autoriteit 4%

Opmerking: dit specifieke antwoord is nietvoor het converteren van numerieke factoren naar numerieke waarden, het is voor het converteren van categorische factoren naar hun corresponderende niveaunummers.


Elk antwoord in dit bericht leverde geen resultaten voor mij op, er werden NA’s gegenereerd.

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

Wat voor mij werkte, is dit –

as.integer(y2)
# [1] 1 2 3 4 1

Antwoord 5

Je kunt hablar::convertgebruiken als je een dataframe hebt. De syntaxis is eenvoudig:

Voorbeeld df

library(hablar)
library(dplyr)
df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

Oplossing

df %>% 
  convert(num(a, b))

geeft u:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

Of als u wilt dat één kolom een geheel getal en één numeriek is:

df %>% 
  convert(int(a),
          num(b))

resulteert in:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30

Antwoord 6

te laat in het spel, ontdekte ik per ongeluk dat trimws()factor(3:5)kan converteren naar c("3","4","5"). Dan kun je as.numeric()aanroepen. Dat is:

as.numeric(trimws(x_factor_var))

Antwoord 7

type.convert(f)op een factor waarvan de niveaus volledig numeriek zijn, is een andere basisoptie.

Performance-wijze is het ongeveer gelijk aan as.numeric(as.character(f))maar niet bijna net zo snel als as.numeric(levels(f))[f].

identical(type.convert(f), as.numeric(levels(f))[f])
[1] TRUE

Dat gezegd hebbende, als de reden dat de vector in eerste instantie als factor is gemaakt, is niet aangepakt (dat wil zeggen dat het waarschijnlijk enkele personages bevatte die niet aan numeriek konden worden gedwongen), dan zal deze aanpak niet werken en het zal terugkeren en het zal terugkeren een factor.

levels(f)[1] <- "some character level"
identical(type.convert(f), as.numeric(levels(f))[f])
[1] FALSE

8

strtoi()WERKEN Als uw factorwaarden gehele getallen zijn.


9

Uit de vele antwoorden die ik kon lezen, was de enige gegeven manier om het aantal variabelen uit te breiden volgens het aantal factoren. Als u een variabele “PET” hebt met niveaus “hond” en “CAT”, zou u eindigen met PET_DOG en PET_CAT.

In mijn geval wilde ik bij hetzelfde aantal variabelen blijven, door gewoon de factorvariabele te vertalen naar een numerieke, op een manier die op veel variabelen met veel niveaus kan worden toegepast, zodat Cat = 1 en hond = 0 bijvoorbeeld.

Zoek hieronder de bijbehorende oplossing:

crime <- data.frame(city = c("SF", "SF", "NYC"),
                    year = c(1990, 2000, 1990),
                    crime = 1:3)
indx <- sapply(crime, is.factor)
crime[indx] <- lapply(crime[indx], function(x){ 
  listOri <- unique(x)
  listMod <- seq_along(listOri)
  res <- factor(x, levels=listOri)
  res <- as.numeric(res)
  return(res)
}
)

10

Het lijkt erop dat de oplossing as.numeric(levels(f))[f] niet meer werkt met R 4.0.

Alternatieve oplossing:

factor2number <- function(x){
    data.frame(levels(x), 1:length(levels(x)), row.names = 1)[x, 1]
}
factor2number(yourFactor)

Other episodes