наиболее эффективный способ преобразования данных категориальных символов в разреженную матрицу в R? [Дубликат]

Когда вы пишете [x]*3, вы получаете, по существу, список [x, x, x]. То есть список с 3 ссылками на тот же x. Когда вы затем изменяете этот сингл x, он отображается через все три ссылки на него.

Чтобы исправить это, вам нужно убедиться, что вы создаете новый список в каждой позиции. Один из способов сделать это -

[[1]*4 for n in range(3)]

, который будет повторно оценивать [1]*4 каждый раз, а не оценивать его один раз и делать 3 ссылки на 1 список.


Вы можете удивиться почему * не может создавать независимые объекты так, как это делает понимание списка. Это потому, что оператор умножения * работает с объектами, не видя выражений. Когда вы используете * для умножения [[1] * 4] на 3, * видит только 1-элементный список [[1] * 4], а не текст выражения [[1] * 4. * не имеет понятия, как делать копии этого элемента, не знаю, как переоценить [[1] * 4], и не подозревайте, что вы даже хотите копировать, и вообще, возможно, даже не было способа скопировать элемент.

Единственный вариант * - это сделать новые ссылки на существующий подсписчик вместо того, чтобы пытаться создавать новые подсписки. Все остальное было бы непоследовательным или требовало бы значительного пересмотра основополагающих решений по языковому дизайну.

Напротив, понимание списка переоценивает выражение элемента на каждой итерации. [[1] * 4 for n in range(3)] пересчитывает [1] * 4 каждый раз по той же причине [x**2 for x in range(3)] каждый раз переоценивает x**2. Каждая оценка [1] * 4 генерирует новый список, поэтому понимание списка делает то, что вы хотели.

Кстати, [1] * 4 также не копирует элементы [1], но это не имеет значения , так как целые числа неизменны. Вы не можете сделать что-то вроде 1.value = 2 и превратить 1 в 2.

5
задан Matthew Lundberg 16 May 2014 в 05:44
поделиться

3 ответа

Спасибо за разъяснение вашего вопроса, попробуйте это.

Вот пример данных с двумя столбцами, которые имеют три и два уровня соответственно:

set.seed(123)
n <- 6
df <- data.frame(x = sample(c("A", "B", "C"), n, TRUE),
                 y = sample(c("D", "E"),      n, TRUE))
#   x y
# 1 A E
# 2 C E
# 3 B E
# 4 C D
# 5 C E
# 6 A D

library(Matrix)
spm <- lapply(df, function(j)sparseMatrix(i = seq_along(j),
                                          j = as.integer(j), x = 1))
do.call(cBind, spm)
# 6 x 5 sparse Matrix of class "dgCMatrix"
#               
# [1,] 1 . . . 1
# [2,] . . 1 . 1
# [3,] . 1 . . 1
# [4,] . . 1 1 .
# [5,] . . 1 . 1
# [6,] 1 . . 1 .

Изменить: @ user20650 указал, что do.call(cBind, ...) вяло или не работает с большими данными. Итак, вот более сложный, но гораздо более быстрый и эффективный подход:

n <- nrow(df)
nlevels <- sapply(df, nlevels)
i <- rep(seq_len(n), ncol(df))
j <- unlist(lapply(df, as.integer)) +
     rep(cumsum(c(0, head(nlevels, -1))), each = n)
x <- 1
sparseMatrix(i = i, j = j, x = x)
8
ответ дан flodel 19 August 2018 в 01:22
поделиться
  • 1
    Это очень быстро! Мне также понравился метод Бена, но эта последняя версия, которую вы создали с точки зрения пользователя, немного быстрее. Большое спасибо за помощь! – agondiken 14 April 2014 в 12:50
  • 2
    Привет, флопель, отличный ответ. Если do.call работает не так, как насчет Reduce(cBind, spm)? – Ping Jin 17 August 2016 в 04:46

Это можно сделать немного более компактно с Matrix:::sparse.model.matrix, хотя требование иметь все столбцы для всех переменных делает вещи немного сложнее.

Сгенерировать вход:

set.seed(123)
n <- 6
df <- data.frame(x = sample(c("A", "B", "C"), n, TRUE),
                 y = sample(c("D", "E"),      n, TRUE))

Если вам не нужны все столбцы для всех переменных, которые вы могли бы просто сделать:

library(Matrix)
sparse.model.matrix(~.-1,data=df)

Если вам нужны все столбцы:

fList <- lapply(names(df),reformulate,intercept=FALSE)
mList <- lapply(fList,sparse.model.matrix,data=df)
do.call(cBind,mList)
3
ответ дан Ben Bolker 19 August 2018 в 01:22
поделиться
  • 1
    @ Бен; Мне было интересно посмотреть на производительность этого (объявление Flodels) на проблему, масштабируемую для данных OP. Если df определен df <- data.frame(replicate(1000,sample(letters[1:15], 100, TRUE))), ни одно из ваших решений не запускается: с ошибкой на do.call Error in match.fun(FUN) : node stack overflow. Это проблема или на моем ПК. благодаря – user20650 13 April 2014 в 16:28
  • 2
    на моем ноутбуке, создающем fList, почти мгновенно; создание mList занимает около 6 секунд; а затем я получаю переполнение стека узлов. Однако, если я делаю system.time(X <- Reduce(cBind,mList)), это занимает 3 секунды, а результат - разреженная матрица размером 100 x 14985. Возможно, стоит связаться с сопровождающим Matrix ... – Ben Bolker 13 April 2014 в 17:01
  • 3
    @ Бен; спасибо за подтверждение - так это проблема с тем, как cBind работает при вызове в do.call при большом? – user20650 13 April 2014 в 17:13
  • 4
    Думаю да. (Вы можете легко протестировать / исследовать это, используя cBind на все более крупных подмножествах разреженных подматриц и посмотреть, где он ломается ...) – Ben Bolker 13 April 2014 в 17:49

Добавление комментария в качестве ответа, поскольку оно кажется немного более быстрым и более масштабируемым (по крайней мере, на моем ПК (ubuntu R3.1.0)

Matrix(model.matrix(~ -1 + . , data=df, 
         contrasts.arg = lapply(df, contrasts, contrasts=FALSE)),sparse=TRUE)

Тестирование с большими данными

library(Matrix)
library(microbenchmark)

set.seed(123)
df <- data.frame(replicate(200,sample(letters[1:15], 100, TRUE)))

ben <- function() {
  fList <- lapply(names(df),reformulate,intercept=FALSE)
  do.call(cBind,lapply(fList,sparse.model.matrix,data=df))
  }


flodel <- function(){
  do.call(cBind,lapply(df, function(j)sparseMatrix(i = seq_along(j),
                                        j = as.integer(j), x = 1)))    
   }


user <- function(){
  Matrix(model.matrix(~ -1 + . , data=df, 
                  contrasts.arg = lapply(df, contrasts, contrasts=FALSE)),
     sparse=TRUE)
   }


    microbenchmark(flodel(), flodel2(), ben(), user(),times=10)
# Unit: milliseconds
 #     expr        min         lq    median         uq        max neval
  # flodel() 1002.79714 1005.70631 1100.1874 1179.84403 1192.56583    10
  # flodel2()   16.62579   17.37707   18.5620   18.72137   19.19888    10
  #     ben() 1602.80193 1612.45177 1616.6684 1703.16246 1709.90557    10
  #    user()   96.80575   97.37132  101.9881  104.00750  195.87784    10

Изменить добавление обновления flodel - его clear - v. nice

3
ответ дан user20650 19 August 2018 в 01:22
поделиться
  • 1
    где есть определение для flodel2? – cerd 21 September 2015 в 23:17
  • 2
    @cerd; см. редактирование flodels для кода, который начинается & quot; Edit: @ user20650 point ... & quot; – user20650 21 September 2015 в 23:22
Другие вопросы по тегам:

Похожие вопросы: