pythonnumpyreshape

Reshape object with shape (500,)


I'm using the following code to reshape an object with shape (625,):

u1 = np.linspace(16, 20, 25)
u2 = np.linspace(16, 20, 25)
u1, u2 = np.meshgrid(u1, u2)
U = np.vstack((u1.flatten(), u2.flatten()))
Z = Z.reshape(u1.shape)

resulting in a shape of (25,25).

How can I reshape an object with shape (500,)?

u1 = np.linspace(16, 20, math.sqrt(500)) 

Solution

  • Here is a way to get as close to a square matrix as possible. This is equivalent to the problem of finding two whole factors of n: p, q, such that p * q == n and p as close as possible to sqrt(n).

    That question is not new. For example, it has been posed on Math StackExchange here, where it is established that there is no "quick way" of doing this (e.g. see this fantastic comment or the answer's argument about this being the strength of RSA).

    On StackOverflow, there is a similar question here.

    Edit this is less subtle but faster (at least for small enough n):

    def np_squarishrt(n):
        a = np.arange(1, isqrt(n), dtype=int)
        b = n // a
        i = np.where(a * b == n)[0][-1]
        return a[i], b[i]
    

    Original answer

    Below, we write an algorithm that uses a simple factorization for n, and a search through all the possible combinations of factors (powerset) but without repeat.

    First, a factorization that uses the Sieve of Eratosthenes (e.g. from here):

    def factors(n):
        while n > 1:
            for i in range(2, n + 1):
                if n % i == 0:
                    n //= i
                    yield i
                    break
    

    Then, a modified powerset that emits only unique combinations:

    from itertools import chain, combinations
    
    def uniq_powerset(iterable):
        """
        Similar to powerset(it) but without repeats.
        
        uniq_powerset([1,1,2]) --> (), (1,), (2,), (1, 1), (1, 2), (1, 1, 2)"""
        s = list(iterable)
        return chain.from_iterable(set(combinations(s, r)) for r in range(len(s)+1))
    

    And finally, our function that finds the "squarish root" of n:

    from math import isqrt
    
    def squarishrt(n):
        p = isqrt(n)
        if p**2 == n:
            return p, p
        bestp = 1
        f = list(factors(n))
        for t in uniq_powerset(f):
            if 2 * len(t) > len(f):
                break
            p = np.prod(t) if t else 1
            q = n // p
            if p > q:
                p, q = q, p
            if p > bestp:
                bestp = p
        return bestp, n // bestp
    

    For example:

    >>> squarishrt(500)
    20, 25
    

    You can use this directly to reshape, e.g.:

    a = np.arange(500)
    b = a.reshape(squarishrt(len(a)))
    >>> b.shape
    20, 25
    

    Note, BTW, that a greedy search through the factors doesn't necessarily yields the best solution. For example:

    >>> list(factors(500))
    [2, 2, 5, 5, 5]
    

    And the best combination of them is p = 2 * 2 * 5 (first 3 factors) and q = 5 * 5 (last three).

    By contrast:

    >>> list(factors(300))
    [2, 2, 3, 5, 5]
    

    for which the closest-to-square factorization is p = 3 * 5 and q = 2 * 2 * 5.