Related
I'd like to solve a multivariate linear regression equation for vector X with m elements while I have n observations, Y. If I assume the measurements have Gaussian random errors. How can I solve this problem using python? My problem looks like this:
A simple example of W when m=5, is given as follows:
P.S. I would like to consider the effect of errors and precisely I want to measure the standard deviation of errors.
You can do it like this
def myreg(W, Y):
from numpy.linalg import pinv
m, n = Y.shape
k = W.shape[1]
X = pinv(W.T.dot(W)).dot(W.T).dot(Y)
Y_hat = W.dot(X)
Residuals = Y_hat - Y
MSE = np.square(Residuals).sum(axis=0) / (m - 2)
X_var = (MSE[:, None] * pinv(W.T.dot(W)).flatten()).reshape(n, k, k)
Tstat = X / np.sqrt(X_var.diagonal(axis1=1, axis2=2)).T
return X, Tstat
demo
W = np.array([
[ 1, -1, 0, 0, 1],
[ 0, -1, 1, 0, 0],
[ 1, 0, 0, 1, 0],
[ 0, 1, -1, 0, 1],
[ 1, -1, 0, 0, -1],
])
Y = np.array([2, 4, 1, 5, 3])[:, None]
X, V = myreg(W, Y)
I am a noobie to python and numpy (and programming in general). I am trying to speed up my code as much as possible. The math involves several summations over multiple axes of a few arrays. I've attained one level of vectorization, but I can't seem to get any deeper than that and have to resort to for loops (I believe there's three levels of recursion, M, N, and I, one of which I've eliminated, I). Here's my code for the relevant section (this code works, but I'd like to speed it up):
def B1(n, i):
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
for n in N:
B[n, :] = B1(n, I)
for m in M:
for n in N:
C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
A = np.linalg.solve(C[1:, 1:], (0.25)*Y[1:])
dmaxi is just a float and m, n and i are integers. The arrays have the following shapes:
>>> qi.shape
(551,)
>>> N.shape
(18,)
>>> M.shape
(18,)
>>> I.shape
(551,)
>>> Iq.shape
(1, 551, 3)
>>> U.shape
(1, 551, 3)
As you can see I've vectorized the calculation of the 2nd axis of B, but I can't seem to do it for the 1st axis, C, and Y, which still require the for loops. It seems that when I try to do the same form of vectorization that I did for the 1st axis of B (define a function, then give the array as the argument), I get a broadcasting error since it appears to be trying to calculate both axes simultaneously, rather than the 1st, then the 2nd, which is why I had to force it into a for loop instead. The same problem occurs for both C and Y which is why they're both in for loops also. In case that's confusing, essentially what I tried was:
>>> B[:, :] = B1(N, I)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "sasrec_v6.py", line 155, in B1
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
ValueError: operands could not be broadcast together with shapes (18) (551)
Vectorizing the 2nd axis of B made a substantial improvement to the speed of my code, so I'm assuming that the same will apply for further vectorization (I hope I'm using that term correctly by the way).
You can use broadcasting to make 2d arrays from your 1d index vectors. I haven't tested these yet, but they should work:
If you reshape the N to be a column vector, then B1 will return a 2d array:
B[N] = B1(N[:, None], I)
For Y and C, I'd use np.einsum to have better control over which axes are mulitplied (probably this could be done with np.dot as well but I'm not sure how.
C[M[:, None], N] = np.einsum('ij,kj->ik',
B[M]/np.square(qi*Iq[0, :, 2]),
B[N])
Y[M] = np.einsum('i, ki->k',
U[0, :, 1]/np.square(qi*Iq[0, :, 2]),
B[M])
To see what that indexing trick does:
In [1]: a = np.arange(3)
In [2]: a
Out[2]: array([0, 1, 2])
In [3]: a[:, None]
Out[3]:
array([[0],
[1],
[2]])
In [4]: b = np.arange(4,1,-1)
In [5]: b
Out[5]: array([4, 3, 2])
In [6]: a[:, None] * b
Out[6]:
array([[0, 0, 0],
[4, 3, 2],
[8, 6, 4]])
It saves two orders of magnitude in time:
In [92]: %%timeit
....: B = np.zeros((18, 551))
....: C = np.zeros((18, 18))
....: Y = np.zeros((18))
....: for n in N:
....: B[n, :] = B1(n, I)
....: for m in M:
....: for n in N:
....: C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
....: Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
....:
100 loops, best of 3: 15.8 ms per loop
In [93]: %%timeit
....: Bv = np.zeros((18, 551))
....: Cv = np.zeros((18, 18))
....: Yv = np.zeros((18))
....: Bv[N] = B1(N[:, None], I)
....: Cv[M[:, None], N] = np.einsum('ij,kj->ik', B[M]/np.square(qi*Iq[0, :, 2]), B[N])
....: Yv[M] = np.einsum('i, ki->k', U[0, :, 1]/np.square(qi*Iq[0, :, 2]), B[M])
....:
1000 loops, best of 3: 1.34 ms per loop
Here's my test:
import numpy as np
# make fake data:
np.random.seed(5)
qi = np.random.rand(551)
N = np.random.randint(0,18,18)#np.arange(18)
M = np.random.randint(0,18,18)#np.arange(18)
I = np.arange(551)
Iq = np.random.rand(1, 551, 3)
U = np.random.rand(1, 551, 3)
B = np.zeros((18, 551))
C = np.zeros((18, 18))
Y = np.zeros((18))
Bv = np.zeros((18, 551))
Cv = np.zeros((18, 18))
Yv = np.zeros((18))
dmaxi = 1.
def B1(n, i):
return np.pi * n * dmaxi * (-1)**(n+1) * np.sin(qi[i]*dmaxi) * ((np.pi*n)**2 - (qi[i]*dmaxi)**2)**(-1)
for n in N:
B[n, :] = B1(n, I)
for m in M:
for n in N:
C[m, n] = np.dot((1/np.square(qi*Iq[0, :, 2]))*B[m, :], B[n, :])
Y[m] = np.dot((1/np.square(qi*Iq[0, :, 2]))*U[0, :, 1], B[m, :])
Bv[N] = B1(N[:, None], I)
print "B correct?", np.allclose(Bv, B)
# np.einsum test case:
n, m = 2, 3
a = np.arange(n*m).reshape(n,m)*8 + 2
b = np.arange(n*m)[::-1].reshape(n,m)
c = np.empty((n,n))
for i in range(n):
for j in range(n):
c[i,j] = np.dot(a[i],b[j])
cv = np.einsum('ij,kj->ik', a, b)
print "einsum test successful?", np.allclose(c,cv)
Cv[M[:, None], N] = np.einsum('ij,kj->ik',
B[M]/np.square(qi*Iq[0, :, 2]),
B[N])
print "C correct?", np.allclose(Cv, C)
Yv[M] = np.einsum('i, ki->k',
U[0, :, 1]/np.square(qi*Iq[0, :, 2]),
B[M])
print "Y correct?", np.allclose(Yv, Y)
output :D
B correct? True
einsum test successful? True
C correct? True
Y correct? True
I am programming a turn-based strategy, like Fire Emblem, game in Python using pygame, but I am running into a problem with movement of players. The way that it works is that you select a player and it will show you all allowed moves highlighted in Blue, but my problem is that I am having a problem coming up with a list for all possible moves.
def showPerson(tilex, tiley, personAtTile):
global ALLOWEDMOVES
ALLOWEDMOVES = []
prepare = {k:v for v,k in PLAYERSPOSITION.items()}
z = PLAYERDISTANCE[personAtTile]
#get all coords for the possible moves
currentNewSpots = []
oldSpots = []
a = PLAYERSPOSITION[personAtTile][0]
b = PLAYERSPOSITION[personAtTile][1]
c = a + 1
test = findTileLetter(c, b)
if test:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = a -1
test = findTileLetter(c, b)
if test:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = b + 1
test = findTileLetter(a, c)
if test:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
c = b - 1
test = findTileLetter(a, c)
if test:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
for x in range(PLAYERDISTANCE[prepare[(tilex, tiley)]]):
for y in range(len(currentNewSpots) - 1):
a = currentNewSpots[y][0]
b = currentNewSpots[y][1]
c = a + 1
test = findTileLetter(c, b)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = a -1
test = findTileLetter(c, b)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = b + 1
test = findTileLetter(a, c)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
c = b - 1
test = findTileLetter(a, c)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
Because I don't know what you have in mind, I'll proceed under the following assumptions:
findTileLetter(x, y) tells me if the square at (x, y) is passable. That is, it tells me if a unit could pass through or end their turn on that square.
Units can step up to PLAYERDISTANCE[unit] + 1 times per turn. Each step can be taken up, down, left, or right. Diagonal steps are not allowed, but instead must be accomplished by stepping e.g. left and up.
With that in mind, we note that on the line
for y in range(len(currentNewSpots) - 1):
you iterate over one fewer element of currentNewSpots than you should, and thus every x loop you omit stepping from the element in currentNewSpots that was added last in the preceding x loop. Hence you leave out potential destination squares.
Changing the line to
for y in range(len(currentNewSpots))
fixes this issue.
Additionally, your delta-y tests in the y loop are not quite right:
c = b + 1
test = findTileLetter(a, c)
if test and ((c, b)) not in ALLOWEDMOVES: ### <--- should be (a, c)
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
Blob of working test code follows. The grid defines a world of tiles: 0 tiles are impassable, while 1 tiles are passable. The MY_X and MY_Y define where we're searching from. After every step, we output the map to stdout, illustrating which squares we've found so far.
import sys
MY_X = 3
MY_Y = 4
MY_RNG = 2
grid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 0, 1, 1, 0, 0, 1],
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
def findTileLetter(x, y):
return grid[y][x]
class Person:
pass
def showMap():
for y in range(len(grid)):
for x in range(len(grid[y])):
if grid[y][x] == 0:
sys.stdout.write(' ')
elif x == MY_X and y == MY_Y:
sys.stdout.write('x')
elif (x, y) in ALLOWEDMOVES:
sys.stdout.write('o')
else:
sys.stdout.write('-')
sys.stdout.write('\n')
me = Person()
ALLOWEDMOVES = []
PLAYERDISTANCE = {}
PLAYERDISTANCE[me] = MY_RNG
PLAYERSPOSITION = {}
PLAYERSPOSITION[me] = (MY_X, MY_Y)
def showPerson(tilex, tiley, personAtTile):
global ALLOWEDMOVES
ALLOWEDMOVES = []
prepare = {k:v for v,k in PLAYERSPOSITION.items()}
z = PLAYERDISTANCE[personAtTile]
#get all coords for the possible moves
currentNewSpots = []
oldSpots = []
a = PLAYERSPOSITION[personAtTile][0]
b = PLAYERSPOSITION[personAtTile][1]
c = a + 1
test = findTileLetter(c, b)
if test:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = a -1
test = findTileLetter(c, b)
if test:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = b + 1
test = findTileLetter(a, c)
if test:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
c = b - 1
test = findTileLetter(a, c)
if test:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
showMap()
for x in range(PLAYERDISTANCE[prepare[(tilex, tiley)]]):
for y in range(len(currentNewSpots)):
a = currentNewSpots[y][0]
b = currentNewSpots[y][1]
c = a + 1
test = findTileLetter(c, b)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = a - 1
test = findTileLetter(c, b)
if test and ((c, b)) not in ALLOWEDMOVES:
currentNewSpots.append((c, b))
ALLOWEDMOVES.append((c, b))
c = b + 1
test = findTileLetter(a, c)
if test and ((a, c)) not in ALLOWEDMOVES:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
c = b - 1
test = findTileLetter(a, c)
if test and ((a, c)) not in ALLOWEDMOVES:
currentNewSpots.append((a, c))
ALLOWEDMOVES.append((a, c))
showMap()
showPerson(MY_X, MY_Y, me)
print ALLOWEDMOVES
Assume I have the following arrays:
N = 8
M = 4
a = np.zeros(M)
b = np.random.randint(M, size=N) # contains indices for a
c = np.random.rand(N) # contains random values
I want to sum the values of c according to the indices provided in b, and store them in a. Writing a loop for this is trivial:
for i, v in enumerate(b):
a[v] += c[i]
Since N can get quite big in my real-world problem I'd like to avoid using python loops, but I can't figure out how to write it as a numpy-statement. Can anyone help me out?
Ok, here some example values:
In [27]: b
Out[27]: array([0, 1, 2, 0, 2, 3, 1, 1])
In [28]: c
Out[28]:
array([ 0.15517108, 0.84717734, 0.86019899, 0.62413489, 0.24357903,
0.86015187, 0.85813481, 0.7071174 ])
In [30]: a
Out[30]: array([ 0.77930596, 2.41242955, 1.10377802, 0.86015187])
import numpy as np
N = 8
M = 4
b = np.array([0, 1, 2, 0, 2, 3, 1, 1])
c = np.array([ 0.15517108, 0.84717734, 0.86019899, 0.62413489, 0.24357903, 0.86015187, 0.85813481, 0.7071174 ])
a = ((np.mgrid[:M,:N] == b)[0] * c).sum(axis=1)
returns
array([ 0.77930597, 2.41242955, 1.10377802, 0.86015187])
I want to repeat elements of an array along axis 0 and axis 1 for M and N times respectively:
import numpy as np
a = np.arange(12).reshape(3, 4)
b = a.repeat(2, 0).repeat(2, 1)
print(b)
[[ 0 0 1 1 2 2 3 3]
[ 0 0 1 1 2 2 3 3]
[ 4 4 5 5 6 6 7 7]
[ 4 4 5 5 6 6 7 7]
[ 8 8 9 9 10 10 11 11]
[ 8 8 9 9 10 10 11 11]]
This works, but I want to know are there better methods without create a temporary array.
You could use the Kronecker product, see numpy.kron:
>>> a = np.arange(12).reshape(3,4)
>>> print(np.kron(a, np.ones((2,2), dtype=a.dtype)))
[[ 0 0 1 1 2 2 3 3]
[ 0 0 1 1 2 2 3 3]
[ 4 4 5 5 6 6 7 7]
[ 4 4 5 5 6 6 7 7]
[ 8 8 9 9 10 10 11 11]
[ 8 8 9 9 10 10 11 11]]
Your original method is OK too, though!
You can make use of np.broadcast_to here:
def broadcast_tile(a, h, w):
x, y = a.shape
m, n = x * h, y * w
return np.broadcast_to(
a.reshape(x, 1, y, 1), (x, h, y, w)
).reshape(m, n)
broadcast_tile(a, 2, 2)
array([[ 0, 0, 1, 1, 2, 2, 3, 3],
[ 0, 0, 1, 1, 2, 2, 3, 3],
[ 4, 4, 5, 5, 6, 6, 7, 7],
[ 4, 4, 5, 5, 6, 6, 7, 7],
[ 8, 8, 9, 9, 10, 10, 11, 11],
[ 8, 8, 9, 9, 10, 10, 11, 11]])
Performance
Functions
def chris(a, h, w):
x, y = a.shape
m, n = x * h, y * w
return np.broadcast_to(
a.reshape(x, 1, y, 1), (x, h, y, w)
).reshape(m, n)
def alex_riley(a, b0, b1):
r, c = a.shape
rs, cs = a.strides
x = np.lib.stride_tricks.as_strided(a, (r, b0, c, b1), (rs, 0, cs, 0))
return x.reshape(r*b0, c*b1)
def paul_panzer(a, b0, b1):
r, c = a.shape
out = np.empty((r, b0, c, b1), a.dtype)
out[...] = a[:, None, :, None]
return out.reshape(r*b0, c*b1)
def wim(a, h, w):
return np.kron(a, np.ones((h,w), dtype=a.dtype))
Setup
import numpy as np
import pandas as pd
from timeit import timeit
res = pd.DataFrame(
index=['chris', 'alex_riley', 'paul_panzer', 'wim'],
columns=[5, 10, 20, 50, 100, 500, 1000],
dtype=float
)
a = np.arange(100).reshape((10,10))
for f in res.index:
for c in res.columns:
h = w = c
stmt = '{}(a, h, w)'.format(f)
setp = 'from __main__ import h, w, a, {}'.format(f)
res.at[f, c] = timeit(stmt, setp, number=50)
Output
Since the result cannot be implemented as a view, as_strided offers no benefits over simple preallocation and broadcasting. Because of its overhead as_strided seems in fact a bit slower (I did no proper benchmarking, though).
The as_strided code is taken from #AlexRiley's post.
from numpy.lib.stride_tricks import as_strided
import numpy as np
def tile_array(a, b0, b1):
r, c = a.shape # number of rows/columns
rs, cs = a.strides # row/column strides
x = as_strided(a, (r, b0, c, b1), (rs, 0, cs, 0)) # view a as larger 4D array
return x.reshape(r*b0, c*b1) # create new 2D array
def tile_array_pp(a, b0, b1):
r, c = a.shape
out = np.empty((r, b0, c, b1), a.dtype)
out[...] = a[:, None, :, None]
return out.reshape(r*b0, c*b1)
a = np.arange(9).reshape(3, 3)
kwds = {'globals': {'f_ar': tile_array, 'f_pp': tile_array_pp, 'a': a},
'number': 1000}
from timeit import timeit
print('as_strided', timeit('f_ar(a, 100, 100)', **kwds))
print('broadcast ', timeit('f_pp(a, 100, 100)', **kwds))
Sample run:
as_strided 0.048387714981799945
broadcast 0.04324757700669579
Another solution is to use as_strided. kron is much slower then using repeat twice. I have found that as_strided is much faster than a double repeat in many cases (small arrays [<250x250] with only a doubling in each dimension as_strided was slower). The as_strided trick is as follows:
a = arange(1000000).reshape((1000, 1000)) # dummy data
from numpy.lib.stride_tricks import as_strided
N, M = 4,3 # number of time to replicate each point in each dimension
H, W = a.shape
b = as_strided(a, (H, N, W, M), (a.strides[0], 0, a.strides[1], 0)).reshape((H*N, W*M))
This works by using 0-length strides which causes numpy to read the same value multiple times (until it gets to the next dimension). The final reshape does copy the data, but only once unlike using a double repeat which will copy the data twice.
Errata: I'm only taking 2x upsampling into account.
TL;DR It turns out that after the OpenCV version,
np.repeat(np.repeat(a, 2, axis=1), 2, axis=0)
is the fastest. So the answer is - there's no faster ways in numpy today,
but you can get a slight improvement by changing the order of axes.
And if you don't mind OpenCV -
cv.resize(a, None, fx=2, fy=2, interpolation=cv.INTER_NEAREST)
Here is the test.
import timeit
import numpy as np
import cv2 as cv
test = np.zeros((16, 16, 3), dtype=np.float32)
def measure(f):
t = timeit.timeit("f(test)", number=1000, globals={"test": test, "f": f})
print("%s - %f"%(f.__name__, t))
return f, t
def fastest(c):
print(c.__name__)
winner, t = min((measure(getattr(c, ve)) for ve in dir(c) if ve.startswith("alg_")), key=lambda x: x[1])
print("%s winner: %s - %f"%(c.__name__, winner.__name__, t))
return winner
#fastest
class nn:
def alg_01(a):
return np.repeat(np.repeat(a, 2, axis=0), 2, axis=1)
def alg_02(a):
return np.repeat(np.repeat(a, 2, axis=1), 2, axis=0)
def alg_03(a):
b = a[:, None, :, None]
b = np.concatenate((b, b), axis=1)
b = np.concatenate((b, b), axis=3)
return b.reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_04(a):
b = a[:, None, :, None]
b = np.concatenate((b, b), axis=3)
b = np.concatenate((b, b), axis=1)
return b.reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_05(a):
return (a[:, None, :, None]*np.ones((1, 2, 1, 2)+((1,)*len(a.shape[2:])), dtype=np.float32)).reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_06(a):
return cv.resize(a, None, fx=2, fy=2, interpolation=cv.INTER_NEAREST)
def alg_07(a):
return a[:, None, :, None][:, (0, 0)][:, :, :, (0, 0)].reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_08(a):
return a[:, None, :, None][:, :, :, (0, 0)][:, (0, 0)].reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_09(a):
return np.kron(a, np.ones((2, 2), dtype=np.float32))
def alg_10(a):
return np.broadcast_to(a[:, None, :, None], (a.shape[0], 2, a.shape[1], 2)+a.shape[2:]).reshape(a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:])
def alg_11(a):
ret = np.empty((a.shape[0], 2, a.shape[1], 2, *a.shape[2:]), dtype=np.float32)
ret[...] = a[:, None, :, None]
ret.resize((a.shape[0]<<1, a.shape[1]<<1, *a.shape[2:]), refcheck=False)
return ret
The result is:
nn
alg_01 - 0.040967
alg_02 - 0.033744
alg_03 - 0.057969
alg_04 - 0.048739
alg_05 - 0.076595
alg_06 - 0.078638
alg_07 - 0.084692
alg_08 - 0.084539
alg_09 - 0.344339
alg_10 - 0.078707
alg_11 - 0.049424
nn winner: alg_02 - 0.033744