【问题标题】:Creating m*n arrays from an array and a boolean "shared" array从一个数组和一个布尔“共享”数组创建 m*n 数组
【发布时间】:2021-11-22 19:40:46
【问题描述】:

问题:我正在尝试基于“打包”或更短的主向量 V 生成 n 元素的 m 向量em>,比 m x n 短,以及一个长度为 n 的布尔向量,用于确定元素如何重复。 (向量在下面有更多解释)。主向量的创建方式以及使用的结果仅在必须遵守格式(主向量和布尔值,结果为 m x n)方面相关。

例如,如果元素 0 的布尔值为 False,则所有 m 向量对于元素 0 将具有相同的值,V[0]。如果元素 1 的布尔值为 True,则向量 0 将具有来自 V[1] 的元素 1,但向量 1 将具有来自 V[6] 的元素 1。一个主向量,V,属于;

(1,2,3,4,5,6,10,30,40,60,100,300,400,600)

和一个布尔向量

1, 0, 1, 1, 0, 1

应该产生三个结果向量;

[1 2 3 4 5 6]
[10.  2. 30. 40.  5. 60.]
[100.   2. 300. 400.   5. 600.]

它们共享一些元素,但不共享其他元素。我有一个方法,但它依赖于嵌套循环和 if 语句。 我尝试过的:一个有效但效率低下的示例,其中包含 6 个元素的 3 个结果向量:

import numpy as np

p = np.array((1,2,3,4,5,6,10,30,40,60,100,300,400,600))
genome = np.array((1, 0, 1, 1, 0, 1))

index = 0
for i in range(0,3):
    
    if i==0:
        pBase = p[0:genome.size]
        print(pBase)
    else:
        extra = np.zeros(genome.size)
        for j in range(0,genome.size):
            if genome[j]==True:
                extra[j] = p[genome.size+index]
                index += 1
        pSplit = np.where(genome==False, pBase, extra)
        print(pSplit)

返回(如预期):

[1 2 3 4 5 6]
[10.  2. 30. 40.  5. 60.]
[100.   2. 300. 400.   5. 600.]

每个循环耗时 45.1 µs ± 2.4 µs。对于假设的简单操作来说,这似乎不必要地冗长和缓慢,但我不知道任何替代方法。是否有一些列表推导或替代函数的组合可以以更快、更 Python 的方式完成相同的结果?

编辑:V 的值并不总是像 V10^i 那样简单,给定的向量仅用于演示。这些值可以被认为是任意的(从另一种方法生成,没有像 10^i 这样的可复制模式)。

【问题讨论】:

  • 我不懂逻辑。 [6]来自哪里?
  • 欢迎来到 Stack Overflow!如果代码有效并且您正在寻找改进它的建议,Code Review 是合适的地方。但请先查看codereview.meta.stackexchange.com/questions/5777/…
  • 我理解以下正确吗:对于第一个向量,它采用第一个 len(bolean vector) 元素。对于剩余的向量,如果布尔向量中的值为零,则它只取 V 中与构造向量中相同位置的数字。如果它在布尔向量中为 1,则从 V 中获取下一个尚未添加的元素。
  • 您确定输入正确吗? 50500 不是不见了吗?
  • Barmar:[6] 是第一次需要“新元素”。由于原始数组有 6 个元素,索引为 0:5,第一个向量之后的第一次拉出新元素的向量将从位置 6 开始。第二次需要新值时将从位置 7 开始,依此类推。蜘蛛说得对。 Mozway:永远不需要 50 和 500,因为该元素是“共享的”,或者始终是该位置的第一个向量的值。

标签: python python-3.x numpy


【解决方案1】:

这个程序以另一种方式工作,以支持不是 10 次方的向量。它首先生成向量中的基数,然后根据需要添加尽可能多的向量。向量的生成方式如下:程序构造向量直到remain 中没有更多元素,这主要是达到了最大可构造值。 首先,它复制vector[0]。然后,它遍历基因组向量的所有元素。如果元素为 1,则将新向量中的元素替换为 remain 的第一个值,其中包含所有尚未使用的 V 值。

V=[1,2,3,4,5,6,10,30,40,60,100,300,400,600]
genome=[1,0,1,1,0,1]
vectors=[V[:len(genome)]]
remain=V[len(genome):]
genome_sum=sum(genome)
while len(remain)>=genome_sum:# no more vectors constructable
    newv=vectors[0].copy()
    for i,x in enumerate(genome):   
        if x==1:
            newv[i]=remain[0]
            del remain[0]
    
    vectors.append(newv)

【讨论】:

  • 我已经更新了代码,提高了效率。我还更改了变量名称,以使它们更具代表性。
  • 每个循环 13.8 µs ± 1.24 µs,约快 3 倍,但不如 Plonetheus 的回答快。谢谢!
【解决方案2】:

你可以试试这个:

import numpy as np

V = np.array((1, 2, 3, 4, 5, 6, 10, 30, 40, 60, 100, 300, 400, 600))
b = np.array([1, 0, 1, 1, 0, 1]).astype(bool)

nc = len(b)
nr = (len(V) - len(b)) // b.sum() + 1
out = np.tile(V[:nc], reps=(nr, 1))
out[1:][np.tile(b, reps=(nr - 1, 1))] = V[nc:]
print(out)

它给出:

[[  1   2   3   4   5   6]
 [ 10   2  30  40   5  60]
 [100   2 300 400   5 600]]

【讨论】:

  • 非常干净!每个循环我得到 35.6 µs ± 1.11 µs,速度提高了约 30%。谢谢!
  • @torridbeaver 我很惊讶矢量化代码的性能会优于纯 Python 代码,因此我自己运行了一些测试。对于小型输入数组,我在很大程度上获得了与您相同的时序结果。但是,当输入数组较大时(例如,布尔数组中的 100 个元素,另一个中的几百个元素),矢量化代码比非矢量化代码快几倍。因此,对于小型数组,调用 numpy 函数会减慢代码速度,但对于任何较大的数组,numpy 的效率要高得多,正如人们所期望的那样。
【解决方案3】:

事实证明,经过一些准备(只需 5 行代码) 您的任务可以通过单个指令来执行。

第 1 部分:准备步骤:

gs1 = genome.size             # Number of elements
gs2 = genome.sum()            # Number of "True" values
rowNo = (p.size - gs1) // gs2 # Number of rows to be filled "with spaces"
# Indices of "true" in "genome", repeated in as many rows as needed
idx_g = np.tile(np.where(genome)[0], rowNo).reshape(rowNo, -1)
res = np.tile(p[0:gs1], rowNo + 1).reshape(rowNo + 1, -1)   # The result (initial content)

第 2 部分:实际计算:

np.put_along_axis(res[1:],    # Destination array
    idx_g,                    # Target indices in each row 
    p[gs1:].reshape(-1, gs2), # "Following" part of "p" cut into rows
    axis=1)                   # The axis to take 1D slices along

有关详细信息,请参阅各个 Numpy 方法的文档。

对于您的源数据,结果(res 数组)是:

[[  1   2   3   4   5   6]
 [ 10   2  30  40   5  60]
 [100   2 300 400   5 600]]

【讨论】:

  • 我不确定我是否遵循 - 第 1 部分中的“res”似乎已经有了正确的答案(太棒了!)但我从第 2 部分得到“无”作为回报。
  • np.put_along_axis 不返回任何内容。如有任何疑问,请查看此功能的文档。结果在 res 数组中。执行后检查 res 包含的内容。
【解决方案4】:

如果我正确理解了这个问题,我会以更简洁的方式执行以下任务,但您可以让我知道您的想法。

def convert_vectors(master_vector, boolean_vector):
    """
    example:
    master_vector = [1,2,3,4,5,6,10,30,40,60,100,300,400,600]
    boolean_vector = [1,0,1,1,0,1]
    result = [[1, 2, 3, 4, 5, 6],[10, 2, 30, 40, 5, 60],[100, 2, 300, 400, 5, 600]]
    """
    res = []  # result
    curIndexInMaster = 0  # index in master_vector
    while curIndexInMaster < len(master_vector):
        curArray = []  # current array
        for bool in boolean_vector:  # for each element in boolean_vector
            if bool:  # should get new element from master_vector
                curArray.append(master_vector[curIndexInMaster])
                curIndexInMaster += 1
            else:
                curArray.append(master_vector[len(curArray)])
                if curIndexInMaster < len(boolean_vector):  # only for first array
                    curIndexInMaster += 1
        res.append(curArray)
    return res


master_vector = [1, 2, 3, 4, 5, 6, 10, 30, 40, 60, 100, 300, 400, 600]
boolean_vector = [1, 0, 1, 1, 0, 1]
print(convert_vectors(master_vector, boolean_vector))

输出:

[[1, 2, 3, 4, 5, 6], [10, 2, 30, 40, 5, 60], [100, 2, 300, 400, 5, 600]]

【讨论】:

  • 3.91 µs ± 405 ns 每个循环,快一个数量级。谢谢,我想你已经找到答案了。
  • 你不应该使用内置函数或类名(如bool)作为变量名。
猜你喜欢
  • 1970-01-01
  • 2019-12-07
  • 1970-01-01
  • 2012-12-16
  • 2017-05-06
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
相关资源
最近更新 更多