Як перетворити множник на цілий \ числовий без втрати інформації?


598

Коли я перетворюю коефіцієнт у числове чи ціле число, я отримую коди базового рівня, а не значення як числа.

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

Я маю вдатися, pasteщоб отримати реальні цінності:

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

Чи є кращий спосіб перетворити множник на числовий?


6
Рівні фактора так чи інакше зберігаються як тип даних символів ( attributes(f)), тому я не думаю, що тут нічого поганого as.numeric(paste(f)). Можливо, було б краще подумати, чому (в конкретному контексті) ви отримуєте фактор в першу чергу, і спробуйте це зупинити. Наприклад, чи правильно встановлений decаргумент read.table?
CJB

Якщо ви використовуєте фрейм даних, ви можете використовувати перетворення з хаблара. df %>% convert(num(column)). Або якщо у вас є факторний вектор, який ви можете використовуватиas_reliable_num(factor_vector)
davsjob

Відповіді:


711

Дивіться розділ Попередження ?factor:

Зокрема, as.numericзастосовувати до фактора безглуздо і може статися за неявного примусу. Для перетворення коефіцієнта fприблизно в його початкові числові значення as.numeric(levels(f))[f]рекомендується і трохи ефективніше, ніж as.numeric(as.character(f)).

У FAQ на R є аналогічні поради .


Чому as.numeric(levels(f))[f]ефективніше, ніж as.numeric(as.character(f))?

as.numeric(as.character(f))ефективно as.numeric(levels(f)[f]), тому ви здійснюєте перетворення в числове length(x)значення, а не nlevels(x)значення. Різниця швидкостей буде найбільш очевидною для довгих векторів з кількома рівнями. Якщо значення здебільшого унікальні, різниці в швидкості не буде. Однак ви зробите конверсію, ця операція навряд чи буде вузьким місцем у вашому коді, тому не переживайте над цим.


Деякі таймінги

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

4
Тимчаси дивіться у цій відповіді: stackoverflow.com/questions/6979625/…
Арі Б. Фрідман

3
Велике спасибі за ваше рішення. Чи можу я запитати, чому as.numeric (рівні (f)) [f] є більш точним та швидшим? Дякую.
Сем

7
@Sam as.character (f) вимагає "примітивного пошуку", щоб знайти функцію as.character.factor (), яка визначається як.numeric (рівні (f)) [f].
Джонатан

12
коли застосувати as.numeric (Level (f)) [f] OR as.numeric (as.character (f)), у мене з'являється повідомлення попередження: Попереджувальне повідомлення: NA, введені примусово. Чи знаєте ви, де може бути проблема? Дякую тобі !
майкка

@maycca Ви подолали це питання?
користувач08041991

91

R має ряд (недокументованих) функцій зручності для перетворення факторів:

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

Але прикро, нічим не вдається обробити фактор -> числове перетворення. Як продовження відповіді Джошуа Ульріха, я б запропонував подолати це упущення визначенням власної ідіоматичної функції:

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

що ви можете зберігати на початку свого сценарію, а ще краще у своєму .Rprofileфайлі.


14
Немає що обробляти перетворення коефіцієнта на ціле (або числове), оскільки, як очікується, as.integer(factor)поверне основні цілі коди (як показано в розділі прикладів ?factor). Мабуть, добре визначити цю функцію у вашому глобальному середовищі, але у вас можуть виникнути проблеми, якщо ви фактично зареєструєте її як метод S3.
Джошуа Ульріх

1
Це хороший момент, і я погоджуюся: повне переосмислення множника-> числового перетворення, ймовірно, зіпсує багато речей. Я виявив , що писати громіздкі factor->numericперетворення багато , перш ніж зрозумів , що це насправді недолік R: деякі функції зручності повинні бути доступні ... Виклик це as.numeric.factorмає сенс для мене, але YMMV.
Джалі

4
Якщо ви виявите, що ви робите це багато , то вам слід зробити щось вище, щоб уникнути цього разом.
Джошуа Ульріх

2
as.numeric.factor повертає NA?
jO.

@jO.: у випадках, коли ти використовував щось подібне v=NA;as.numeric.factor(v)або v='something';as.numeric.factor(v), тоді це повинно, інакше у тебе десь відбувається дивна річ.
Джалі

33

Найпростішим способом було б використання unfactorфункції з пакету varhandle

unfactor(your_factor_variable)

Цей приклад може бути швидким початком:

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"

У unfactorфункції звернені до типу символьних даних, а потім перетворює назад в числовий. Наберіть unfactorна консолі, і ви зможете побачити її посередині функції. Тому насправді це не дає кращого рішення, ніж те, що вже ставив запитувач.
CJB

Сказавши це, рівні фактора так чи інакше мають характер характеру, тому таким підходом нічого не втрачається.
CJB

unfactorФункція піклується про речі , які не можуть бути перетворені в числовий. Перегляньте приклади вhelp("unfactor")
Мехрад Махмудіян

2
@Selrac Я вже говорив , що ця функція доступна в varhandle упаковці означає, що ви повинні завантажити пакет ( library("varhandle")) першим (як я вже говорив в першому рядку моєї відповіді !!)
Mehrad Mahmoudian

1
@Gregor додаючи залежність від світла зазвичай не шкодить, і звичайно, якщо ви шукаєте найефективніший спосіб, написання коду, яке ваше самоврядування може виконувати швидше. але як ви також можете бачити у своєму коментарі, це не тривіально, оскільки ви також ставите as.numeric()і as.character()в неправильному порядку;) Що ваш фрагмент коду робить, це перетворити індекс рівня фактора в матрицю символів, так що у вас буде в - символьний вектор, який містить деякі числа, які колись були присвоєні певному рівню вашого фактора. Функції в цьому пакеті є для запобігання цих плутанин
Мехрад Махмудян

23

Примітка: ця конкретна відповідь не для перетворення числових коефіцієнтів у числові, а для перетворення категоричних факторів у відповідні їм рівні рівня.


Кожна відповідь у цій публікації не дала результатів для мене, НС отримували результати.

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

Що для мене працювало - це

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

Ви впевнені, що у вас був фактор? Подивіться на цей приклад. y<-factor(c("5","15","20","2")); unclass(y) %>% as.numericЦе повертає 4,1,3,2, а не 5,15,20,2. Це здається невірною інформацією.
MrFlick

Гаразд, це схоже на те, що я намагався сьогодні зробити: - y2 <-factor (c ("A", "B", "C", "D", "A")); as.numeric (рівні (y2)) [y2] [1] NA NA NA NA NA Попереджувальне повідомлення: NA введено примусово, тоді як unclass (y2)%>% as.numeric дав мені необхідні мені результати.
Indi

4
Гаразд, це не питання, яке задавали вище. У цьому питанні рівні коефіцієнтів є "числовими". У вашому випадку, as.numeric(y)мав би працювати добре, не потрібно unclass(). Але знову ж таки, це не про це. Ця відповідь тут не підходить.
MrFlick

3
Ну, я дуже сподіваюся, що це допоможе тому, хто поспішав, як я, і прочитав лише заголовок!
Інді

1
Якщо у вас є символи, що представляють цілі числа як фактори, це я рекомендую. це єдиний, хто працював на мене.
ціль

9

Це можливо лише в тому випадку, коли мітки факторів відповідають вихідним значенням. Я поясню це на прикладі.

Припустимо, дані є векторними x:

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

Тепер я створять фактор з чотирма мітками:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) xє з типом double, fє з цілим числом типу. Це перша неминуча втрата інформації. Фактори завжди зберігаються як цілі числа.

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) Повернутись до початкових значень (10, 20, 30, 40) неможливо лише з fнаявними. Ми можемо бачити, що fмістить лише цілі значення 1, 2, 3, 4 та два атрибути - список міток ("A", "B", "C", "D") та атрибут класу "factor". Нічого більше.

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

Щоб повернутися до початкових значень, ми повинні знати значення рівнів, що використовуються при створенні фактора. У цьому випадку c(10, 20, 30, 40). Якщо ми знаємо початкові рівні (у правильному порядку), ми можемо повернутись до початкових значень.

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

І це буде працювати лише в тому випадку, коли мітки були визначені для всіх можливих значень у вихідних даних.

Тож якщо вам знадобляться початкові значення, вам доведеться їх зберігати. Інакше є велика ймовірність, що повернутися до них не вдасться лише з фактора.


2

Ви можете використовувати, hablar::convertякщо у вас є кадр даних. Синтаксис простий:

Зразок df

library(hablar)
library(dplyr)

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

Рішення

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

дає вам:

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

Або якщо ви хочете, щоб один стовпець був цілим, а один числовим:

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

призводить до:

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

0

Виглядає, що рішення, як. числовий (рівні (f)) [f] більше не працює з R 4.0.

Альтернативне рішення:

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

factor2number(yourFactor)

-1

З багатьох відповідей, які я міг прочитати, єдиним даним способом було розширення кількості змінних відповідно до кількості факторів. Якщо у вас є змінний "вихованець" з рівнями "собака" і "кішка", ви б закінчилися з pet_dog і pet_cat.

У моєму випадку я хотів залишитися з однаковою кількістю змінних, просто переклавши змінну фактора на числову, таким чином, що можна застосувати до багатьох змінних з багатьма рівнями, так що, наприклад, cat = 1 і dog = 0.

Знайдіть відповідне рішення нижче:

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)
}
)

-2

пізно до гри, випадково, я виявив , trimws()може конвертувати factor(3:5)в c("3","4","5"). Тоді ви можете зателефонувати as.numeric(). Це є:

as.numeric(trimws(x_factor_var))

3
Чи є причина , ви б рекомендував використовувати trimwsбільш , as.characterяк описано в загальноприйнятому відповідь? Мені здається, що, якщо ви насправді не мали пробілу, який потрібно було вилучити, trimwsпросто збираєтеся робити купу непотрібних регулярних виразів, щоб повернути той самий результат.
MrFlick

as.numeric (рівні (f)) [f] може бути дещо заплутаним і важко запам'ятати початківцям. trimws не шкодить.
Джері Т
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.