【问题标题】:how to calculate the Euclidean norm of a vector in R?如何计算R中向量的欧几里得范数?
【发布时间】:2012-06-11 15:12:07
【问题描述】:

我试过norm,但我认为它给出了错误的结果。 (c(1, 2, 3) 的标准是sqrt(1*1+2*2+3*3),但它返回6..

x1 <- 1:3
norm(x1)
# Error in norm(x1) : 'A' must be a numeric matrix
norm(as.matrix(x1))
# [1] 6
as.matrix(x1)
#      [,1]
# [1,]    1
# [2,]    2
# [3,]    3
norm(as.matrix(x1))
# [1] 6

有谁知道在 R 中计算向量范数的函数是什么?

【问题讨论】:

  • "norm" 并不是你想象的那样。试试sqrt(sum(x^2))。 R 做“你所期望的”。 normdist 旨在提供矩阵行之间的广义距离计算。
  • 这将返回一个向量,其中每个分量的平方根为平方,因此 1 2 3 而不是欧几里得范数

标签: r vector statistics


【解决方案1】:
norm(c(1,1), type="2")     # 1.414214
norm(c(1, 1, 1), type="2")  # 1.732051

【讨论】:

  • 这是正确的答案,它也允许 R 使用它的内部优化。
  • 这比 jorah 对我的回答要花更多的时间。请参阅 AbdealiJK 的答案以检查时间。
  • 这不是关于速度,而是关于避免上溢/下溢
  • 对于破坏性的上溢或下溢问题,使用带缩放的规范。请参阅下面的答案。看knorm()的函数定义
【解决方案2】:

这是一个你自己写的微不足道的函数:

norm_vec <- function(x) sqrt(sum(x^2))

【讨论】:

  • 嘿,你从上面的评论侵犯了我的版权!我将派出一支 RIAA 律师团队追随你。 :-)
  • @CarlWitthoft 我刚去支付了一些版税,所以希望我们都是方方正正的。 :)
  • 非常不同意这个答案。在 R 中,如果有可用的内置函数,您几乎总是希望使用它。它们是高度优化的。 Bernd的答案是正确的答案。如果您遇到此问题,请向下滚动并使用适当的 R 函数来执行此操作。
  • @Dalupus 在做出如此有力的陈述之前,您应该费心对这两种解决方案进行基准测试。您可能会发现结果令人惊讶。当我这样做时,我发现我的版本快了大约 5 倍。 (这并不是说可能没有理由使用norm。但你应该在强烈发言之前检查一下。)
  • 这不是真正的速度问题。试试norm_vec(c(10^200))norm(c(10^200), type="2") 看看有什么区别。
【解决方案3】:

我很惊讶没有人尝试分析上述建议方法的结果,所以我这样做了。我使用了一个随机统一函数来生成一个列表并将其用于重复(只是一个简单的信封类型基准测试):

> uut <- lapply(1:100000, function(x) {runif(1000, min=-10^10, max=10^10)})
> norm_vec <- function(x) sqrt(sum(x^2))
> norm_vec2 <- function(x){sqrt(crossprod(x))}
> 
> system.time(lapply(uut, norm_vec))
   user  system elapsed 
   0.58    0.00    0.58 
> system.time(lapply(uut, norm_vec2))
   user  system elapsed 
   0.35    0.00    0.34 
> system.time(lapply(uut, norm, type="2"))
   user  system elapsed 
   6.75    0.00    6.78 
> system.time(lapply(lapply(uut, as.matrix), norm))
   user  system elapsed 
   2.70    0.00    2.73 

似乎至少对于实数值向量而言,手动获取电源然后 sqrt 比内置 norm 更快。这可能是因为 norm 在内部做了一个 SVD:

> norm
function (x, type = c("O", "I", "F", "M", "2")) 
{
    if (identical("2", type)) {
        svd(x, nu = 0L, nv = 0L)$d[1L]
    }
    else .Internal(La_dlange(x, type))
}

SVD 函数在内部将向量转换为矩阵,并做更复杂的事情:

> svd
function (x, nu = min(n, p), nv = min(n, p), LINPACK = FALSE) 
{
    x <- as.matrix(x)
    ...

编辑(2019 年 10 月 20 日):

已经有一些cmets指出了上述测试用例没有提出的正确性问题:

> norm_vec(c(10^155))
[1] Inf
> norm(c(10^155), type="2")
[1] 1e+155

这是因为大数在 R 中被视为无穷大:

> 10^309
[1] Inf

所以,它看起来像:

对于实数值向量对于小数而言,似乎先取权然后手动 sqrt 比内置规范要快。

有多小?这样平方和就不会溢出了。

【讨论】:

  • 我喜欢这个问题从“我如何让 R 做到这一点?”到优化。我也刚刚在 R 中创建了自己的距离函数,然后好奇内置函数是什么以及为什么只尝试“norm(v)”不起作用哈哈。我还检查了我的功能的速度..
【解决方案4】:
norm(x, type = c("O", "I", "F", "M", "2"))

默认为"O"

"O""o""1" 指定一个范数,(最大绝对列总和);

“F”或“f”指定 Frobenius 范数(将 x 的欧几里得范数视为向量);

norm(as.matrix(x1),"o")

结果为6,与norm(as.matrix(x1))相同

norm(as.matrix(x1),"f")

结果是sqrt(1*1+2*2+3*3)

所以,norm(as.matrix(x1),"f") 就是答案。

【讨论】:

    【解决方案5】:

    我们也可以找到范数:

    Result<-sum(abs(x)^2)^(1/2)
    

    或者你也可以尝试如下:

    Result<-sqrt(t(x)%*%x)
    

    两者都会给出相同的答案

    【讨论】:

    • 两个化简:如果x的分量是实数,可以把abs(x)^2换成x^2。同样,%*% 根据需要转置向量,因此您可以将t(x)%*%x 简化为x%*%x
    【解决方案6】:

    我也要把它作为等效的 R 表达式扔出去

    norm_vec(x) <- function(x){sqrt(crossprod(x))}
    

    不要将 R 的 crossprod 与名称相似的向量 /cross product 混淆。众所周知,这种命名会导致confusion,尤其是对于那些具有物理/力学背景的人。

    【讨论】:

    • 完全正确,我写的代码会按照你说的做,但我真的只是想强调向量范数计算。我将在这里遵循 Joran 的命名约定。好建议。
    【解决方案7】:

    如果您有一个 data.frame 或一个 data.table 'DT',并且想要计算每一行的欧几里得范数(范数 2),可以使用 apply 函数。

    apply(X = DT, MARGIN = 1, FUN = norm, '2')
    

    例子:

    >DT 
    
            accx       accy       accz
     1: 9.576807 -0.1629486 -0.2587167
     2: 9.576807 -0.1722938 -0.2681506
     3: 9.576807 -0.1634264 -0.2681506
     4: 9.576807 -0.1545590 -0.2681506
     5: 9.576807 -0.1621254 -0.2681506
     6: 9.576807 -0.1723825 -0.2682434
     7: 9.576807 -0.1723825 -0.2728810
     8: 9.576807 -0.1723825 -0.2775187
    
    > apply(X = DT, MARGIN = 1, FUN = norm, '2')
     [1] 9.581687 9.582109 9.581954 9.581807 9.581932 9.582114 9.582245 9.582378
    

    【讨论】:

      【解决方案8】:

      带有缩放以避免破坏性下溢和溢出的向量的欧几里得长度(k-范数)的答案是

      norm <- function(x, k) { max(abs(x))*(sum((abs(x)/max(abs(x)))^k))^(1/k) }
      

      解释见下文。

      1.没有缩放的向量的欧几里得长度:


      norm() 是一个向量值函数,用于计算向量的长度。它需要两个参数,例如matrix 类的向量xinteger 类的范数类型k

      norm <- function(x, k) {
        # x = matrix with column vector and with dimensions mx1 or mxn
        # k = type of norm with integer from 1 to +Inf
        stopifnot(k >= 1) # check for the integer value of k greater than 0
        stopifnot(length(k) == 1) # check for length of k to be 1. The variable k is not vectorized.
        if(k == Inf) {
          # infinity norm
          return(apply(x, 2, function(vec) max(abs(vec)) ))
        } else {
          # k-norm
          return(apply(x, 2, function(vec) (sum((abs(vec))^k))^(1/k) ))
        }
      }
      
      x <- matrix(c(1,-2,3,-4)) # column matrix
      sapply(c(1:4, Inf), function(k) norm(x = x, k = k))
      # [1] 10.000000  5.477226  4.641589  4.337613  4.000000
      
      • 1-范数 (10.0) 收敛到无穷大范数 (4.0)。
      • k-norm也称为“欧几里得n维空间中的欧几里得范数”。

      注意:norm() 函数定义中,对于具有实分量的向量,绝对值可以在 norm-2k 甚至索引范数中丢弃,其中k &gt;= 1

      如果您对 norm 函数定义感到困惑,您可以单独阅读下面给出的每一个。

      norm_1 <- function(x) sum(abs(x))
      norm_2 <- function(x) (sum((abs(x))^2))^(1/2)
      norm_3 <- function(x) (sum((abs(x))^3))^(1/3)
      norm_4 <- function(x) (sum((abs(x))^4))^(1/4)
      norm_k <- function(x) (sum((abs(x))^k))^(1/k)
      norm_inf <- max(abs(x))
      

      2。带有缩放的向量的欧几里得长度以避免破坏性上溢和下溢问题:


      注 2: 这个解决方案norm() 的唯一问题是它不能防止herehere 提到的上溢或下溢问题。

      幸运的是,有人已经在 blas(基本线性代数子程序)fortran 库中解决了 2 范数(欧几里得长度)的问题。可以在“Kahaner、Moler 和 Nash 的数值方法和软件”教科书 - Chapter-1, Section 1.3, page - 7-9 中找到有关此问题的描述。

      fortran 子例程的名称是dnrm2.f,它通过缩放向量分量的最大值来处理norm() 中的破坏性上溢和下溢问题。由于norm()函数中的激进操作,会出现破坏性上溢和下溢问题。

      我将在下面的R 中展示如何实现dnrm2.f

      #1. find the maximum among components of vector-x
      max_x <- max(x)
      #2. scale or divide the components of vector by max_x
      scaled_x <- x/max_x
      #3. take square of the scaled vector-x
      sq_scaled_x <- (scaled_x)^2
      #4. sum the square of scaled vector-x
      sum_sq_scaled_x <- sum(sq_scaled_x)
      #5. take square root of sum_sq_scaled_x
      rt_sum_sq_scaled_x  <- sqrt(sum_sq_scaled_x)
      #6. multiply the maximum of vector x with rt_sum_sq_scaled_x
      max_x*rt_sum_sq_scaled_x
      

      dnrm2.fR中的上述6个步骤的单行是:

      # Euclidean length of vector - 2norm
      max(x)*sqrt(sum((x/max(x))^2))
      

      让我们尝试示例向量来计算此问题的 2 范数(请参阅此线程中的其他解决方案)。

      x = c(-8e+299, -6e+299, 5e+299, -8e+298, -5e+299)
      max(x)*sqrt(sum((x/max(x))^2))
      # [1] 1.227355e+300
      
      x <- (c(1,-2,3,-4))
      max(x)*sqrt(sum((x/max(x))^2))
      # [1] 5.477226
      

      因此,在 R 中实现 k 范数的通用解决方案的推荐方法是单行,它可以防止破坏性上溢或下溢问题。为了改进这一单行,您可以使用norm() 的组合而不缩放包含不太小或不太大的分量的向量,以及使用knorm() 缩放包含太小或太小的向量大型组件。对所有向量实施缩放会导致计算量过多。我没有在下面给出的knorm() 中实现这一改进。

      # one-liner for k-norm - generalized form for all norms including infinity-norm:
      max(abs(x))*(sum((abs(x)/max(abs(x)))^k))^(1/k)
      
      # knorm() function using the above one-liner.
      knorm <- function(x, k) { 
        # x = matrix with column vector and with dimensions mx1 or mxn
        # k = type of norm with integer from 1 to +Inf
        stopifnot(k >= 1) # check for the integer value of k greater than 0
        stopifnot(length(k) == 1) # check for length of k to be 1. The variable k is not vectorized.
        # covert elements of matrix to its absolute values
        x <- abs(x)
        if(k == Inf) { # infinity-norm
          return(apply(x, 2, function(vec) max(vec)))
        } else { # k-norm
          return(apply(x, 2, function(vec) {
            max_vec <- max(vec)
            return(max_vec*(sum((vec/max_vec)^k))^(1/k))
          }))
        }
      }
      
      # 2-norm
      x <- matrix(c(-8e+299, -6e+299, 5e+299, -8e+298, -5e+299))
      sapply(2, function(k) knorm(x = x, k = k))
      # [1] 1.227355e+300
      
      # 1-norm, 2-norm, 3-norm, 4-norm, and infinity-norm
      sapply(c(1:4, Inf), function(k) knorm(x = x, k = k))
      # [1] 2.480000e+300 1.227355e+300 9.927854e+299 9.027789e+299 8.000000e+299
      
      x <- matrix(c(1,-2,3,-4))
      sapply(c(1:4, Inf), function(k) knorm(x = x, k = k))
      # [1] 10.000000  5.477226  4.641589  4.337613  4.000000
      
      x <- matrix(c(1,-2,3,-4, 0, -8e+299, -6e+299, 5e+299, -8e+298, -5e+299), nc = 2)
      sapply(c(1:4, Inf), function(k) knorm(x = x, k = k))
      #           [,1]          [,2]          [,3]          [,4]   [,5]
      # [1,]  1.00e+01  5.477226e+00  4.641589e+00  4.337613e+00  4e+00
      # [2,] 2.48e+300 1.227355e+300 9.927854e+299 9.027789e+299 8e+299
      

      【讨论】:

      • 好答案。您的单线只是缺少x 是零向量的情况。
      【解决方案9】:

      按照 AbdealiJK 的回答,

      我进一步试验以获得一些见解。

      这是一个。

      x = c(-8e+299, -6e+299, 5e+299, -8e+298, -5e+299)
      sqrt(sum(x^2))
      norm(x, type='2')
      

      第一个结果是Inf,第二个结果是1.227355e+300,这是完全正确的,我在下面的代码中显示。

      library(Rmpfr)
      y <- mpfr(x, 120)
      sqrt(sum(y*y))    
      

      结果是1227354879...。我没有计算尾随数字的数量,但看起来还不错。我知道解决这个OVERFLOW 问题的另一种方法是首先将日志函数应用于所有数字并总结,我没有时间实现!

      【讨论】:

        【解决方案10】:

        使用 cbind 将矩阵创建为立柱虎钳,然后 norm 函数与 Frobenius norm(欧几里得范数)作为参数很好地配合。

        x1

        标准(x1,"f")

        [1] 3.741657

        sqrt(1*1+2*​​2+3*3)

        [1] 3.741657

        【讨论】:

          猜你喜欢
          • 2021-02-11
          • 2021-01-31
          • 1970-01-01
          • 2021-02-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多