Когда вы пишете [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.
Спасибо за разъяснение вашего вопроса, попробуйте это.
Вот пример данных с двумя столбцами, которые имеют три и два уровня соответственно:
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)
Это можно сделать немного более компактно с 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)
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
fList
, почти мгновенно; создание mList
занимает около 6 секунд; а затем я получаю переполнение стека узлов. Однако, если я делаю system.time(X <- Reduce(cBind,mList))
, это занимает 3 секунды, а результат - разреженная матрица размером 100 x 14985. Возможно, стоит связаться с сопровождающим Matrix
...
– Ben Bolker
13 April 2014 в 17:01
cBind
работает при вызове в do.call
при большом?
– user20650
13 April 2014 в 17:13
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
Reduce(cBind, spm)
? – Ping Jin 17 August 2016 в 04:46