n dimensional grid in Python / numpy

前端 未结 5 696
忘掉有多难
忘掉有多难 2021-01-23 06:57

I have an unknown number n of variables that can range from 0 to 1 with some known step s, with the condition that they sum up to 1. I want to create a

5条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-23 07:32

    EDIT

    Here is a better solution. It basically partitions the number of steps into the amount of variables to generate all the valid combinations:

    def partitions(n, k):
        if n < 0:
            return -partitions(-n, k)
        if k <= 0:
            raise ValueError('Number of partitions must be positive')
        if k == 1:
            return np.array([[n]])
        ranges = np.array([np.arange(i + 1) for i in range(n + 1)])
        parts = ranges[-1].reshape((-1, 1))
        s = ranges[-1]
        for _ in range(1, k - 1):
            d = n - s
            new_col = np.concatenate(ranges[d])
            parts = np.repeat(parts, d + 1, axis=0)
            s = np.repeat(s, d + 1) + new_col
            parts = np.append(parts, new_col.reshape((-1, 1)), axis=1)
        return np.append(parts, (n - s).reshape((-1, 1)), axis=1)
    
    def make_grid_part(n, step):
        num_steps = round(1.0 / step)
        return partitions(num_steps, n) / float(num_steps)
    
    print(make_grid_part(3, 0.33333))
    

    Output:

    array([[ 0.        ,  0.        ,  1.        ],
           [ 0.        ,  0.33333333,  0.66666667],
           [ 0.        ,  0.66666667,  0.33333333],
           [ 0.        ,  1.        ,  0.        ],
           [ 0.33333333,  0.        ,  0.66666667],
           [ 0.33333333,  0.33333333,  0.33333333],
           [ 0.33333333,  0.66666667,  0.        ],
           [ 0.66666667,  0.        ,  0.33333333],
           [ 0.66666667,  0.33333333,  0.        ],
           [ 1.        ,  0.        ,  0.        ]])
    

    For comparison:

    %timeit make_grid_part(5, .1)
    >>> 338 µs ± 2.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    %timeit make_grid_simple(5, .1)
    >>> 26.4 ms ± 806 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    make_grid_simple actually runs out of memory if you push it just a bit further.


    Here is one simple way:

    def make_grid_simple(n, step):
        num_steps = round(1.0 / step)
        vs = np.meshgrid(*([np.linspace(0, 1, num_steps + 1)] * n))
        all_combs = np.stack([v.flatten() for v in vs], axis=1)
        return all_combs[np.isclose(all_combs.sum(axis=1), 1)]
    
    print(make_grid_simple(3, 0.33333))
    

    Output:

    [[ 0.          0.          1.        ]
     [ 0.33333333  0.          0.66666667]
     [ 0.66666667  0.          0.33333333]
     [ 1.          0.          0.        ]
     [ 0.          0.33333333  0.66666667]
     [ 0.33333333  0.33333333  0.33333333]
     [ 0.66666667  0.33333333  0.        ]
     [ 0.          0.66666667  0.33333333]
     [ 0.33333333  0.66666667  0.        ]
     [ 0.          1.          0.        ]]
    

    However, this is not the most efficient way to do it, since it is simply making all the possible combinations and then just picking the ones that add up to 1, instead of generating only the right ones in the first place. For small step sizes, it may incur in too high memory cost.

提交回复
热议问题