将一行转换成combine,c()作为r中的向量,然后用向量计算余弦相似度

Convert a row into a combine, c() as a vector in r and then use vectors to calculate the cosine similarity

你好,我有一个非常大的数据框,它是一个部分:

v1 <- c('i1', 'i10', 'i11')
v2 <- c(0.11, 0.07, 0.114)
v3 <- c(0.07, 0.08, 0.03)
df <- data.frame(cbind(v1, v2, v3))

如何编写一些代码将每一行转换为组合向量,x <- c()

也就是说,我的预期输出应该是并且变量名称需要来自列 V1 :

i1 <- c(0.11014318, 0.07302843, 0.01360761, 0.10619829, 0.14513045)
i10 <- c(0.07360007, 0.08013833, 0.13104657, 0.13174247, 0.14256615)
i11 <- c(0.11418245, 0.03300573, 0.11425297, 0.13686428, 0.03367279)

将每一行转换为向量后,我需要计算这些向量之间的余弦相似度,这就是为什么我需要拆分每一行并将它们保存为具有第一列名称的向量的原因 V1

library(lsa)
cosine(i1, i10)
cosine(i1, i11)
cosine(i10, i11)

下面的问题

你好山姆。感谢您的帮助,但我不知道为什么在添加更多列 V4V5 以及 ID 为 i12 的一行时它不起作用?非常感谢您的耐心等待和帮助。

data_matrix <- function(df){
  data_matrix  <- tail(t(df), -1) |>
    sapply(as.numeric) |>
    matrix(
        nrow = ncol(df)-1, 
        ncol = nrow(df), 
        dimnames = list(
            seq_len(nrow(df)-1), # rows
            df[,1] # columns
        )
    ) 
}

v1 <- c('i1', 'i10', 'i11', 'i12')
v2 <- c(0.11, 0.07, 0.114, 0.67)
v3 <- c(0.07, 0.08, 0.03, 087)
v4 <- c(0.12, 0.13, 0.14, 0.18)
v5 <- c(0.19, 0.21, 0.22, 0.22)
df <- data.frame(cbind(v1, v2, v3, v4, v5))
df

data_matrix(df)

只是 returns 错误:

Error in matrix(sapply(tail(t(df), -1), as.numeric), nrow = ncol(df) -  : 
  length of 'dimnames' [1] not equal to array extent

您可以使用 splitasplit 拆分行,使用 setNames 将列表元素的名称设置为第一列,然后使用 list2env 将列表的元素添加到全局环境中:

l <- setNames(split(df[-1], seq(nrow(df))), df[,1])

# $i1
#     v2   v3
# 1 0.11 0.07
# 
# $i10
#     v2   v3
# 2 0.07 0.08
# 
# $i11
#      v2   v3
# 3 0.114 0.03

list2env(l, .GlobalEnv)

其他拆分选项包括 asplitrow:

asplit(df[-1], 1)
split(df[-1], row(df[-1])[, 1])
as.list(as.data.frame(t(df[, -1])))

另一种方法是在每一行上使用 apply,这样您就可以直接设置环境:

apply(df, 1, function(x) assign(x[1], tail(x, -1), envir = globalenv()))

不过我同意@danlooo 的评论:我想不出任何你想要这样做的理由。

编辑:如何计算余弦相似度矩阵(以下评论)

如果你想计算一个余弦相似度矩阵,最好从一个矩阵开始,而不是弄乱你的全局环境,然后必须进行可能很大的成对计算组合。

首先将数据设置为正确的格式,一个带有列名的数字矩阵,这些列名是数据框的第一列:

data_matrix  <- tail(t(df), -1) |>
    sapply(as.numeric) |>
    matrix(
        nrow = ncol(df) - 1, 
        ncol = nrow(df), 
        dimnames = list(
            seq_len(ncol(df)-1), # rows
            df[,1] # columns
        )
    ) 

data_matrix
#     i1  i10   i11
# 1 0.11 0.07 0.114
# 2 0.07 0.08 0.030

那么计算余弦相似度就很简单了:


library(lsa)
cosine(data_matrix)

#            i1       i10       i11
# i1  1.0000000 0.9595950 0.9525148
# i10 0.9595950 1.0000000 0.8283488
# i11 0.9525148 0.8283488 1.0000000

您可以使用 lapply() 遍历所有行并为您的 df 编制索引。

之后,您可以使用@Maël 的list2env函数将列表中的元素保存到全局环境中。

setNames(lapply(1:nrow(df), function(x) df[x, -1]), df[, 1])

$i1
    v2   v3
1 0.11 0.07

$i10
    v2   v3
2 0.07 0.08

$i11
     v2   v3
3 0.114 0.03

先前答案的另一种变体:

lapply(seq_len(nrow(df)), \(.) assign(df$v1[.], unlist(df[.,-1]), envir = .GlobalEnv))

也就是说,对于每一(lapply)行(seq_len(nrow(df))\(.)),将所有列直到第一列转换为向量(unlist(df[.,-1])),然后 assign 这些向量到全局环境 (envir = .GlobalEnv) 中的第一列字符串 (unlist(df[.,-1]))。

更快,改进@SamR 解决方案(其中将 df 转换为数组,所有数字数据都变成字符):

list2env(setNames(apply(df[-1], 1, identity, simplify = FALSE), nm = df$v1), .GlobalEnv)

但不比@Maël 解决方案快

v1 <- paste0("i", 1:1e+3)
lapply(2:200, \(.) assign(paste0("v", .), rnorm(1e+3), envir = .GlobalEnv))
df <- do.call("data.frame", args = sapply(ls(pattern = "^v\d+$"), get, envir = .GlobalEnv, simplify = FALSE))
microbenchmark::microbenchmark(
    list2env(setNames(as.list(as.data.frame(t(df[, -1]))), df[, 1]), .GlobalEnv), 
    list2env(setNames(asplit(df[-1], 1), df[, 1]), .GlobalEnv), 
    list2env(setNames(apply(df[-1], 1, identity, simplify = FALSE), nm = df$v1), .GlobalEnv), 
    check = "equal")
Unit: milliseconds
                                                                                          expr      min       lq     mean   median       uq      max neval
             list2env(setNames(as.list(as.data.frame(t(df[, -1]))), df[, 1]), .GlobalEnv) 5.548269 5.731607 9.444446 5.864418 6.114002 37.83762   100
                               list2env(setNames(asplit(df[-1], 1), df[, 1]), .GlobalEnv) 7.421431 7.568999 9.336666 7.639897 7.800458 31.90791   100
 list2env(setNames(apply(df[-1], 1, identity, simplify = FALSE), nm = df$v1), .GlobalEnv) 8.031275 8.201781 9.796997 8.332828 8.512478 34.35403   100

@Maël 的其他解决方案(使用 split(df[-1], seq(nrow(df)))split(df[-1], row(df[-1])[, 1]))和@benson23 setNames(lapply(1:nrow(df), function(x) df[x, -1]), df[, 1]) 的解决方案产生 data.frame 输出而不是向量。