Python numpy array: Index error, Index out of bounds - python-3.x

The following code is just an example of my original code which is as follows
batch_size = 10
target_q = np.ones((10, 1))
actions = np.ones((10, ), dtype=int)
batch_index = np.arange(batch_size, dtype=np.int32)
print(target_q[batch_index, actions])
print(target_q.shape)
I get the following error
IndexError: index 1 is out of bounds for axis 1 with size 1.
Can someone please explain what this means and how to rectify it.
Thanks in advance.

In numpy you can index arrays of size N up to index N-1 (along a given axis), otherwise you will get the IndexError you are seeing. In order to check how high can you go with an index, you can print target_q.shape. In your case it will tell you (10, 1), which means that if you index target_q[i, j], then i can be maximally 9 and j can be maximally 0.
What you do in your line target_q[batch_index, actions] is you insert actions as so called fancy indexing on the second position (j) and actions is full of ones. Thus, you are trying to many times index with 1, whereas the highest allowed index value is 0.
What would work would be:
import numpy as np
batch_size = 10
target_q = np.ones((10, 1))
# changed to zeros below
actions = np.zeros((10, ), dtype=int)
batch_index = np.arange(batch_size, dtype=np.int32)
print(actions)
print(target_q.shape)
print(target_q[batch_index, 0])
print(target_q[batch_index, actions])
that prints:
[0 0 0 0 0 0 0 0 0 0]
(10, 1)
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

Related

np.where in a loop overwriting all the values

I want to recode the values in my label array so that the labels 0,1,2 correspond to the center values
1.00162877,0.74014188,1.16120161
import numpy as np
label=np.array([0, 2, 1, 1, 2, 1, 0, 0, 1, 2])
center=np.array([[1.00162877],
[0.74014188],
[1.16120161]])
Using the np.where is not overwriting all the values in a single loop but returning 3 different arrays where only a single value is changed and not all.
for i in range(len(center)):
result=np.where(label==[i], center[i], label)
print(result)
[1.00162877 2. 1. 1. 2. 1.
1.00162877 1.00162877 1. 2. ]
[0. 2. 0.74014188 0.74014188 2. 0.74014188
0. 0. 0.74014188 2. ]
[0. 1.16120161 1. 1. 1.16120161 1.
0. 0. 1. 1.16120161]
How to modify the np.where or using any other function that the outcome will look like this.
Expected=([1.00162877,1.16120161,0.74014188,0.74014188,1.1612016,0.74014188,
1.00162877,1.00162877,0.74014188,1.16120161])
This is not a loop but I think it works:
center[label].ravel()
Output:
array([1.00162877, 1.16120161, 0.74014188, 0.74014188, 1.16120161,
0.74014188, 1.00162877, 1.00162877, 0.74014188, 1.16120161])

Random generator in python

I want to select one of 0 or 1 based on some probability of getting 1 and some initial seed.
I tried following:
import random
population = [0,1]
random.seed(33)
probabilities = [0.4,0.2,0.5]
def sampleIt():
selectedProb = random.randrange(0,3,1) #select one of probabilities
print('Selected Probability: ', selectedProb)
return random.choices(population, [0, probabilities[selectedProb-1]])
for i in range(100):
sample = sampleIt()
print(sample[0])
Below is sample output:
Selected Probability: 0.2
1
Selected Probability: 0.5
1
Selected Probability: 0.4
1
Selected Probability: 0.2
1
Selected Probability: 0.5
1
Selected Probability: 0.2
1
Doubts:
As you can see, it is able to randomly select probabilities. But for each selected probability, it ends up selecting 1 from population. If it selected probability 0.2, then I expect it to select 1 with probability 0.2. In this way, it should have selected 0 at least once. But that is not happening. Why is this so?
Is seed correct set or we have to set differently?
Also, what changes I need to do if I expect sampleIt() to be called from different threads?
Also is there any standard practice to improve performance, say if I run this millions of time? Do I have to use numpy for random number generation?
Does random.randrange() and random.choice() follow uniform distribution?
You can run code online here.
There are several critical errors here. Let's talk about that and then the correct way to do this.
First, if this were working properly, you'd be getting 1 with net probability of 0.37, which is 1/3*(0.2 + 0.4 + 0.5) because you are randomly choosing a probability.
You are passing weights to random.choices in the second positional argument, and you are passing a weight of 0 for option zero, so it will never be picked. In that same statement, you are unnecessarily subtracting 1 from the range that you have...
So, to do this properly for Bernoulli trials, you can just draw a random number and compare it to the probability you want. Or you can use random.choices correctly and get a list.
In [14]: def gen_sample(p_success):
...: if random.random() < p_success:
...: return 1
...: return 0
...:
In [15]: gen_sample(0.95)
Out[15]: 1
In [16]: gen_sample(0.02)
Out[16]: 0
In [17]: p_success = 0.85
In [18]: random.choices([0, 1], weights=[1-p_success, p_success], k=10)
Out[18]: [1, 1, 1, 1, 1, 1, 1, 0, 1, 1]

Change Values in One Array, Based On Value from Column of Second Array

I have a one dimensional array called Y_train that contains a series of 1's and 0's. I have another array called sample_weight that is an array of all 1's that has the shape of Y_train, defined as:
sample_weight = np.ones(Y_train.shape, dtype=int)
I'm trying to change the values in sample_weight to a 2, where the corresponding value in Y_train == 0. So initially side by side it looks like:
Y_train sample_weight
0 1
0 1
1 1
1 1
0 1
1 1
and I'd like it to look like this after the transformation:
Y_train sample_weight
0 2
0 2
1 1
1 1
0 2
1 1
What I tried was to use a for loop (shown below) but none of the 1's are changing to 2's in sample_weight. I'd like to somehow use the np.where() function if possible, but it's not crucial, just would like to avoid a for loop:
sample_weight = np.ones(Y_train.shape, dtype=int)
for num, i in enumerate(Y_train):
if i == 0:
sample_weight[num] == 2
I tried using the solution shown here but to no success with the second array. Any ideas??? Thanks!
import numpy as np
Y_train = np.array([0,0,1,1,0,1])
sample_weight = np.where(Y_train == 0, 2, Y_train)
>> print(sample_weight)
[2 2 1 1 2 1]
The np.where basically works just like Excel's "IF":
np.where(condition, then, else)
Works for transposed arrays, too:
Y_train = np.array([[0,0,1,1,0,1]]).T
sample_weight = np.where(Y_train == 0, 2, Y_train)
>> print(sample_weight)
[[2]
[2]
[1]
[1]
[2]
[1]]

How to efficiently append running sum in Python?

I'm writing a python script that uses a model to predict a large number of values by groupID, where efficiency is important (N on the order of 10^8). I initialize a results matrix and am trying to sequentially update a running sum of values in the results matrix.
Trying to be efficient, in my current method I use groupID as row numbers of the results matrix to avoid merging (merging is expensive, as far as I understand).
My attempt:
import numpy as np
# Initialize results matrix
results = np.zeros((5,3)) # dimension: number of groups x timestep
# Now I loop over batches, with batch size 4. Here's an example of one iteration:
batch_groupIDs = [3,1,0,1] # Note that multiple values can be generated for same groupID
batch_results = np.ones((4,3))
# My attempt at appending the results (low dimension example):
results[batch_groupIDs] += batch_results
print(results)
This outputs:
[[1. 1. 1.]
[1. 1. 1.]
[0. 0. 0.]
[1. 1. 1.]
[0. 0. 0.]]
My desired output is the following (since group 1 shows up twice, and should be appended twice):
[[1. 1. 1.]
[2. 2. 2.]
[0. 0. 0.]
[1. 1. 1.]
[0. 0. 0.]]
The actual dimensions of my problem are approximately 100 timesteps x a batch size of 1 million+ and 2000 groupIDs
Here is my understanding, and please correct me if I'm wrong:
We want a resultant matrix that has the shape number of groups x timestep which in this case would be 2000 x 100. This matrix needs to be efficiently updated sequentially for a batch size of 10^6.
If the summary is correct, here is my approach. We divide the 10^6 sequences into let's say "mini-batches" of 10^4 each. Another assumption is that the timesteps are weighted equally across all groups. :
Convert batch_groupIDs to a numpy array of frequencies. For batch_groupIDs = [3,1,0,1], the array would look like [1,2,0,1,0].
Convert the timestep values to numpy array. For batch_results = np.ones((4,3)), timestep array would look like [1,1,1] since 3 steps add equal weight
Find the outer product and add it to the result.
Repeat until all sequences are generated.
Here is the equivalent function:
import random
GROUPIDs = 2000
MINIBATCH_SIZE = 10000
TIMESTEPS = 100
def getMatrixOuter():
result = np.zeros((GROUPIDs,TIMESTEPS))
for x in range(100): #100 mini-batches each of size 10000 to replicate a sequence
batch_groupIDs = [random.randrange(GROUPIDs) for i in range(MINIBATCH_SIZE)] #10000 random sequences in range(0,GROUPIDs)
counts = {k: 0 for k in range(GROUPIDs)}
for i in batch_groupIDs:
counts[i] = counts.get(i, 0) + 1
vals = np.fromiter(counts.values(), dtype=int) #array of frequencies of GROUPIDs
steps=np.ones((TIMESTEPS,)) #weight of each timestep
np.add(np.outer(vals, steps), result, out=result)
return result
Here is how it fared when timed:
%timeit getMatrixOuter()
1 loop, best of 3: 704 ms per loop
A possible benchmark could be evaluating it against a sequential add using np.add.at()
Here is a possible benchmark function:
GROUPIDs = 2000
MINIBATCH_SIZE = 10000
TIMESTEPS = 100
def getMatrixnp():
result = np.zeros((GROUPIDs,TIMESTEPS))
for i in range(int(1e6)):
np.add.at(result, [random.randrange(2000) for i in range(1)], 1)
return result
And here is how it fared on the same system:
%timeit getMatrixnp()
1 loop, best of 3: 10.8 s per loop
The solution certainly rests on some assumptions that may very well turn out to be false. Nevertheless, my 2 cents.
Using pandas, you can count the occurrence of each batch_groupID using the groupby method. Once performed, you can simply add this result to your initial matrix using the add() method (you need to ensure the axis is here is set to 0). If you specifically need a numpy.array, then you can just use the .to_numpy() method on the DataFrame.
import pandas as pd
s = pd.Series(batch_groupIDs)
array = np.zeros((5,3))
#create a new series counting the occurrence of each index
group = s.groupby(s).count()
# leverage pandas add each count occurrence
results = pd.DataFrame(array).add(group, 0).fillna(0).to_numpy()
which gives
array([[1., 1., 1.],
[2., 2., 2.],
[0., 0., 0.],
[1., 1., 1.],
[0., 0., 0.]])

Embedding a tensor of vectors into a tensor of matrices

I want to create multiple matrices that have the property that their diagonal is zero and that are symmetric. Matrices of dimension n of this form need n*(n-1)/2 parameters to be completely specified.
These parameters shall later be learned...
In numpy I'm able to compute these by using numpy.triu_indices to get the indices of the upper triangular matrix starting at the first diagonal above the main diagonal and then fill it by the provided parameters as in the following code snippet:
import numpy as np
R = np.array([[1,2,1,1,2,1], [1,1,1,1,1,1]])
s = R.shape[1]
M = R.shape[0]
iu_r, iu_c = np.triu_indices(s,1)
Q = np.zeros((M,s,s),dtype=float)
Q[:,iu_r,iu_c] = R
Q = Q + np.transpose(Q,(0,2,1))
Output:
[[[0. 1. 2. 1.]
[1. 0. 1. 2.]
[2. 1. 0. 1.]
[1. 2. 1. 0.]]
[[0. 1. 1. 1.]
[1. 0. 1. 1.]
[1. 1. 0. 1.]
[1. 1. 1. 0.]]]
But apparently one can not directly translates this to tensorflow, as
import tensorflow as tf
import numpy as np
M = 2
s = 4
iu_r, iu_c = np.triu_indices(s,1)
rates = tf.get_variable(shape=(M,s*(s-1)/2), name="R", dtype=float)
Q = tf.get_variable(shape=(M,s,s), dtype=float, initializer=tf.initializers.zeros, name="Q")
Q = Q[:,iu_r,iu_c].assign(rates)
fails with
TypeError: Tensors in list passed to 'values' of 'Pack' Op have types [int32, int64, int64] that don't all match.
What would be the correct way to define this tensor of matrices from a tensor of vectors in tensorflow?
EDIT:
My current solution is to embed using the scatter_nd function provided by tensorflow as it fits the need that no redundant variables need to be allocated as in the case of fill_triangular. Though, the indexing is not compatible with the indexes generated by numpy. Currently hardcoded the following example works:
import tensorflow as tf
import numpy as np
M = 2
s = 4
iu_r, iu_c = np.triu_indices(s,1)
rates = tf.get_variable(shape=(M,s*(s-1)/2), name="R", dtype=float)
iupper = [[[0,0,1],[0,0,2],[0,0,3],[0,1,2],[0,1,3],[0,2,3]],[[1,0,1],[1,0,2],[1,0,3],[1,1,2],[1,1,3],[1,2,3]]]
Q = tf.scatter_nd(iupper,rates,shape=(M,s,s), name="rate_matrix")
It should be no problem to translate the indices obtained by
iu_r, iu_c = np.triu_indices(s,1)
But maybe someone has a more elegant solution for that?
This part is unclear to me how it works:
import numpy as np
R = np.array([[1,2,1,1,2,1], [1,1,1,1,1,1]])
s = R.shape[1]
M = R.shape[0]
iu_r, iu_c = np.triu_indices(s,1)
Q = np.zeros((M,s,s),dtype=float)
Q[:,iu_r,iu_c] = R
Q = Q + np.transpose(Q,(0,2,1))
because this will fail in error.
You may use simpler code like this:
import numpy as np
R = [1,2,1,1,2,1]
N = 4
Q = np.zeros((N,N),dtype=float)
for i in range(0,N):
for j in range(0,N):
if (i<j):
Q[i][j] = R.pop(0)
Q would be:
[[0. 1. 2. 1.]
[0. 0. 1. 2.]
[0. 0. 0. 1.]
[0. 0. 0. 0.]]
<class 'numpy.ndarray'>
To get the symmetric Q just use this: Q = Q + np.transpose(Q)
Whatever zigzag you do with your rates later you can convert to Tensors like this:
import tensorflow as tf
data_tf = tf.convert_to_tensor(Q, np.float32)
sess = tf.InteractiveSession()
print(data_tf.eval())
sess.close()
The other answers suggest to use the convert_to_tensor function, to convert your numpy array to a TensorFlow tensor.
This indeed can give you matrices with the desired property of being symmetric with a zero diagonal. However, once you start training, these properties might not hold anymore, as there is no guarantee in general that the weight updates will keep this property fixed.
If you do need to keep the matrices symmetric with a zero diagonal all along the training process, you can do the following:
import tensorflow as tf
from tensorflow.contrib.distributions import fill_triangular
M = 2 # batch size
s = 4 # matrix size
rates = tf.get_variable(shape=(M,s*(s+1)/2), name="R", dtype=float)
# Q will be triangular (with a non-zero diagonal!)
Q = fill_triangular(rates)
# set the diagonal of Q to zero.
Q = tf.linalg.set_diag(Q,tf.zeros((M,s)))
# make Q symmetric
Q = Q + tf.transpose(Q,[0,2,1])
Here is a test that verifies that the matrices hold the required properties, even after training:
import numpy as np
# define some arbitrary loss function
Q_target = tf.constant(np.random.normal(size=(1,s,s)).astype(np.float32))
loss = tf.nn.l2_loss(Q-Q_target)
# a single training step (which will update the matrices)
train_step = tf.train.GradientDescentOptimizer(learning_rate=0.1).minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
# this is Q before training
print(sess.run(Q))
#[[[ 0. -0.564 0.318 -0.446]
# [-0.564 0. -0.028 0.2 ]
# [ 0.318 -0.028 0. 0.369]
# [-0.446 0.2 0.369 0. ]]
#
# [[ 0. 0.412 0.216 0.063]
# [ 0.412 0. 0.221 -0.336]
# [ 0.216 0.221 0. -0.653]
# [ 0.063 -0.336 -0.653 0. ]]]
# this is Q after training
sess.run(train_step)
print(sess.run(Q))
#[[[ 0. -0.548 0.235 -0.284]
# [-0.548 0. -0.055 0.074]
# [ 0.235 -0.055 0. 0.25 ]
# [-0.284 0.074 0.25 0. ]]
#
# [[ 0. 0.233 0.153 0.123]
# [ 0.233 0. 0.144 -0.354]
# [ 0.153 0.144 0. -0.568]
# [ 0.123 -0.354 -0.568 0. ]]]
Apparently you need something like convert_to_tensor.
This function converts Python objects of various types to Tensor objects. It accepts Tensor objects, numpy arrays, Python lists, and Python scalars.
Note: TensorFlow operations automatically convert NumPy ndarrays to Tensors.

Resources