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,)
?
(25,25)
np.linspace
requires integers for the number of samples. The following snippet can't be used:u1 = np.linspace(16, 20, math.sqrt(500))
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
.