有没有办法规范化fcm中的行(即,转换计数为从0到1的值)?

时间:2018-05-07 02:51:32

标签: r sparse-matrix quanteda

美好的一天,

我有一个特征共同位置(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的每一行?

非常感谢!

3 个答案:

答案 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。