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


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.