美好的一天,
我有一个特征共同位置(fcm,来自R中的quanteda包),尺寸为94966 x 94966(为了说明,名为fcm2)。我可以通过功能名称或行号选择行(类:fcm对象),例如:
a1 <- fcm2[1,]
并对该特定行执行规范化计算:
a2 <- a1/(max(a1)-min(a1))
我的目标是规范化原始fcm中的每一行。我尝试的策略是初始化一个空矩阵,然后使用for循环遍历行并执行计算。由于内存问题(Windows 10,12 Gb RAM,R版本3.4.4)初始化失败:
fcm3 <- matrix(data = NA, nrow = 94966, ncol = 94966)
Error: cannot allocate vector of size 33.6 Gb
我已经能够通过使用数据帧结构来完成规范化,但是没有足够的内存来将整个fcm2存储为数据帧:
步骤1:根据关键字列表提取“子矩阵”,转换为数据帧,删除不需要的列
m <- fcm2[keywords(),]
df_m1 <- as.data.frame(m)
df_m1 <- subset(df_m1, select = -c(document, rt))
第2步:标准化
k <- 0 # initialize counter
df2 <- data.frame() # initialize
n4 <- nrow(df_m1) # count rows of the extracted sub-matrix as df (df_m1)
for(k in 1:n4){
a1 <- df_m1[k,] # store the (n4)th row
max_k <- max(a1)
min_k <- min(a1)
a2 <- a1/(max_k-min_k) # normalize so max is 1, 0s are still 0s
df2 <- rbind(df2, a2) # append normalized results into a row of a data.frame
}
是否有更有效的方法来规范整个fcm的每一行?
非常感谢!
答案 0 :(得分:0)
哟可以写一个函数:
norm=function(mat){
mx=mat[cbind(1:nrow(mat),max.col(mat))]
mn=mat[cbind(1:nrow(mat),max.col(-mat))]
mat/(mx-mn)
}
然后使用它。
实施例
set.seed(1)
mat1=matrix(sample(20),5)
mat1
[,1] [,2] [,3] [,4]
[1,] 6 14 3 7 #max is 14, min is 3 thus divide by 11
[2,] 8 15 2 12
[3,] 11 9 20 17
[4,] 16 19 10 18
[5,] 4 1 5 13
norm(mat)
[,1] [,2] [,3] [,4]
[1,] 0.5454545 1.27272727 0.2727273 0.6363636
[2,] 0.6153846 1.15384615 0.1538462 0.9230769
[3,] 1.0000000 0.81818182 1.8181818 1.5454545
[4,] 1.7777778 2.11111111 1.1111111 2.0000000
[5,] 0.3333333 0.08333333 0.4166667 1.0833333
您可以决定以分数形式打印出来,看看结果是否代表了所需的内容:
MASS::fractions(norm(mat))
[,1] [,2] [,3] [,4]
[1,] 6/11 14/11 3/11 7/11
[2,] 8/13 15/13 2/13 12/13
[3,] 1 9/11 20/11 17/11
[4,] 16/9 19/9 10/9 2
[5,] 1/3 1/12 5/12 13/12
答案 1 :(得分:0)
我可以理解OP对内存有约束,并且他无法分配内存来保存那个大matrix
的另一个副本。
如果内存允许,那么解决方案可以是:
mat1 = t(apply(mat1, 1, function(x) x/(max(x)-min(x))))
对于内存约束,人们可以更喜欢编写一个函数来规范化向量并将其应用于for-loop
中的所有行。在给定的场景中它应该是一种有效的方式。
# Function to normalize a vector
normalise <- function(x){
x/(max(x)-min(x))
}
#Apply over all rows of matrix
for(i in 1:nrow(mat1)){
mat1[i,] = normalise(mat1[i,])
}
mat1
# [,1] [,2] [,3] [,4]
# [1,] 0.5454545 1.27272727 0.2727273 0.6363636
# [2,] 0.6153846 1.15384615 0.1538462 0.9230769
# [3,] 1.0000000 0.81818182 1.8181818 1.5454545
# [4,] 1.7777778 2.11111111 1.1111111 2.0000000
# [5,] 0.3333333 0.08333333 0.4166667 1.0833333
数据:由@Onyambu
使用# Data
set.seed(1)
mat1=matrix(sample(20),5)
答案 2 :(得分:0)
最有效方式是直接对fcm对象的稀疏值进行操作,避免任何转换为密集对象(如矩阵或data.frame)。这就是dfm和fcm操作和计算函数在 quanteda 中的定义方式,以及为什么它们能够在有限的内存中快速执行。
要为您的规范化类型定义这样的函数,您可以使用以下函数,我在此处演示了一个简单的fcm。
library("quanteda")
library("Matrix")
myfcm <- fcm(data_char_sampletext, window = 5)
myfcm
## Feature co-occurrence matrix of: 244 by 244 features.
现在我们定义一个函数(为方便起见)将fcm转换为稀疏三元组表示(dgTMatrix类)并使用split()
提取非零值。结果列表的每个元素将代表fcm的一行,但仅适用于非零值。 (因此,我们还必须为空行返回零。)
fcm_customnorm <- function(x) {
x <- as(x, "dgTMatrix")
split_x <- split(x@x, x@i)
norm_x <- lapply(split_x, function(y) {
result <- y/(max(y) - min(y))
# transform any divisions by zero into zero
result[is.nan(result)] <- 0
result
})
x@x <- unlist(norm_x, use.names = FALSE)
quanteda:::as.fcm(x)
}
在子集上应用它,我们看到它有效:
myfcm[1:5, 1:5]
## Feature co-occurrence matrix of: 5 by 5 features.
## 5 x 5 sparse Matrix of class "fcm"
## features
## features Instead we have a Fine
## Instead 0 5 1 4 1
## we 0 10 5 20 5
## have 0 0 0 4 1
## a 0 0 0 6 4
## Fine 0 0 0 0 0
fcm_customnorm(myfcm[1:5, 1:5])
## Feature co-occurrence matrix of: 5 by 5 features.
## 5 x 5 sparse Matrix of class "fcm"
## features
## features Instead we have a Fine
## Instead 0 1.0 0.8000000 0.3333333 1.00
## we 0 0.2 0.2000000 1.3333333 0.25
## have 0 0 0.6666667 0.3333333 3.00
## a 0 0 0 0.0000000 2.00
## Fine 0 0 0 0 0.00
另一种选择是将简单的三元组表示提取到data.table(来自 data.table 包),然后使用分组函数和:=
执行计算。但这种方法更简单,并获得您想要的结果,这是一个标准化的fcm。