9

我想在 numpy 中生成一个秩为 5 的 100x600 矩阵,其中所有条目都是从 np.random.uniform(0, 20) 中采样的,这样所有条目都将均匀分布在 [0, 20) 之间。在 python 中这样做的最佳方法是什么?

我看到这里有一种 SVD 启发的方法(https://math.stackexchange.com/questions/3567510/how-to-generate-a-rank-r-matrix-with-entries-uniform),但是我不知道如何编码。我正在寻找这种受 SVD 启发的方法来获得均匀分布的条目的工作示例。

实际上,我已经设法通过垂直堆叠五个 20x100 秩 1 矩阵,然后对垂直索引进行洗牌来编写一个秩为 5 100x100 的矩阵。但是,生成的 100x100 矩阵没有均匀分布的条目 [0, 20)。

这是我的代码(我最好的尝试):

import numpy as np
def randomMatrix(m, n, p, q):
    # creates an m x n matrix with lower bound p and upper bound q, randomly.
    count = np.random.uniform(p, q, size=(m, n))
    return count

Qs = []
my_rank = 5
for i in range(my_rank):
  L = randomMatrix(20, 1, 0, np.sqrt(20))
  # L is tall
  R = randomMatrix(1, 100, 0, np.sqrt(20)) 
  # R is long
  Q = np.outer(L, R)
  Qs.append(Q)

Q = np.vstack(Qs)
#shuffle (preserves rank 5 [confirmed])
np.random.shuffle(Q)

4

2 回答 2

2

我必须承认,这不是一个完美的解决方案。但这很简单,而且非常接近。
我创建了 5 个将跨越矩阵空间的向量,并创建随机线性组合来填充矩阵的其余部分。我最初的想法是一个简单的解决方案是将这些向量复制 20 次。
为了改善这一点,我创建了它们的线性组合,权重从均匀分布中提取,但随后矩阵中条目的分布变得正常,因为加权平均值基本上导致中心极限定理生效。
普通方法和不起作用的第二种方法之间的中间点是使用一组权重,这些权重有利于其中一个向量而不是其他向量。您可以通过将任何向量传递给具有适当高温参数的 softmax 函数来生成这些类型的权重向量。
分布几乎是均匀的,但向量仍然非常接近基向量。您可以使用温度参数来找到适合您目的的最佳位置。

from scipy.stats import ortho_group
from scipy.special import softmax
import numpy as np
from matplotlib import pyplot as plt
N    = 100
R    = 5
low  = 0
high = 20
sm_temperature = 100

p       = np.random.uniform(low, high, (1, R, N))
weights = np.random.uniform(0, 1, (N-R, R, 1))
weights = softmax(weights*sm_temperature, axis = 1)
p_lc    = (weights*p).sum(1)

rand_mat = np.concatenate([p[0], p_lc])

plt.hist(rand_mat.flatten())

在此处输入图像描述

于 2022-01-18T09:58:38.857 回答
1

我只是不能接受我以前的解决方案(“选择”方法)并没有真正产生严格均匀分布的条目,但有时只是足够接近以欺骗统计测试。然而,渐近的情况几乎肯定不会均匀分布。但我确实想到了另一个同样糟糕的疯狂想法,但以另一种方式——它并不是真正随机的。
在这个解决方案中,我的做法类似于 OP 的方法,即形成秩为 1 的 R 矩阵,然后将它们连接起来,但略有不同。我通过在自身顶部堆叠一个基向量乘以 0.5 来创建每个矩阵,然后将它们堆叠在相同的基向量上,该基向量偏移均匀分布的动态范围的一半。这个过程继续乘以三分之一,三分之二和 1,然后移位等等,直到我在矩阵的那一部分中获得所需向量的数量。
我知道这听起来难以理解。但是,不幸的是,我找不到更好的解释方法。希望通过阅读代码可以了解更多信息。
我希望这种“阶梯式”方法会更加可靠和有用。

import numpy as np 
from matplotlib import pyplot as plt

'''
params:
    N    - base dimention
    M    - matrix length
    R    - matrix rank
    high - max value of matrix
    low  - min value of the matrix
'''
N    = 100
M    = 600
R    = 5
high = 20
low  = 0

# base vectors of the matrix
base = low+np.random.rand(R-1, N)*(high-low)

def build_staircase(base, num_stairs, low, high):
    '''
    create a uniformly distributed matrix with rank 2 'num_stairs' different 
    vectors whose elements are all uniformly distributed like the values of 
    'base'.
    '''
    l = levels(num_stairs)
    vectors = []
    for l_i in l:
        for i in range(l_i):
            vector_dynamic = (base-low)/l_i
            vector_bias    = low+np.ones_like(base)*i*((high-low)/l_i)
            vectors.append(vector_dynamic+vector_bias)
    return np.array(vectors)


def levels(total):
    '''
    create a sequence of stritcly increasing numbers summing up to the total.
    '''
    l = []
    sum_l = 0
    i = 1
    while sum_l < total:
        l.append(i)
        i +=1
        sum_l = sum(l)
    i = 0
    while sum_l > total:
        l[i] -= 1
        if l[i] == 0:
            l.pop(i)
        else:
            i += 1
        if i == len(l):
            i = 0
        sum_l = sum(l)
    return l
        
n_rm = R-1 # number of matrix subsections
m_rm = M//n_rm
len_rms = [ M//n_rm for i in range(n_rm)]
len_rms[-1] += M%n_rm
rm_list = []
for len_rm in len_rms:
    # create a matrix with uniform entries with rank 2
    # out of the vector 'base[i]' and a ones vector.
    rm_list.append(build_staircase(
        base = base[i], 
        num_stairs = len_rms[i], 
        low = low,
        high = high,
    ))

rm = np.concatenate(rm_list)
plt.hist(rm.flatten(), bins = 100)

几个例子:
在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

现在 N = 1000, M = 6000 以经验证明几乎渐近的行为: 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

于 2022-02-02T23:10:26.170 回答