Create 3D array using Python - python

I would like to create a 3D array in Python (2.7) to use like this:
distance[i][j][k]
And the sizes of the array should be the size of a variable I have. (nnn)
I tried using:
distance = [[[]*n]*n]
but that didn't seem to work.
I can only use the default libraries, and the method of multiplying (i.e.,[[0]*n]*n) wont work because they are linked to the same pointer and I need all of the values to be individual

You should use a list comprehension:
>>> import pprint
>>> n = 3
>>> distance = [[[0 for k in xrange(n)] for j in xrange(n)] for i in xrange(n)]
>>> pprint.pprint(distance)
[[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
>>> distance[0][1]
[0, 0, 0]
>>> distance[0][1][2]
0
You could have produced a data structure with a statement that looked like the one you tried, but it would have had side effects since the inner lists are copy-by-reference:
>>> distance=[[[0]*n]*n]*n
>>> pprint.pprint(distance)
[[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
>>> distance[0][0][0] = 1
>>> pprint.pprint(distance)
[[[1, 0, 0], [1, 0, 0], [1, 0, 0]],
[[1, 0, 0], [1, 0, 0], [1, 0, 0]],
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]]

numpy.arrays are designed just for this case:
numpy.zeros((i,j,k))
will give you an array of dimensions ijk, filled with zeroes.
depending what you need it for, numpy may be the right library for your needs.

The right way would be
[[[0 for _ in range(n)] for _ in range(n)] for _ in range(n)]
(What you're trying to do should be written like (for NxNxN)
[[[0]*n]*n]*n
but that is not correct, see #Adaman comment why).

d3 = [[[0 for col in range(4)]for row in range(4)] for x in range(6)]
d3[1][2][1] = 144
d3[4][3][0] = 3.12
for x in range(len(d3)):
print d3[x]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 144, 0, 0], [0, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [3.12, 0, 0, 0]]
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

"""
Create 3D array for given dimensions - (x, y, z)
#author: Naimish Agarwal
"""
def three_d_array(value, *dim):
"""
Create 3D-array
:param dim: a tuple of dimensions - (x, y, z)
:param value: value with which 3D-array is to be filled
:return: 3D-array
"""
return [[[value for _ in xrange(dim[2])] for _ in xrange(dim[1])] for _ in xrange(dim[0])]
if __name__ == "__main__":
array = three_d_array(False, *(2, 3, 1))
x = len(array)
y = len(array[0])
z = len(array[0][0])
print x, y, z
array[0][0][0] = True
array[1][1][0] = True
print array
Prefer to use numpy.ndarray for multi-dimensional arrays.

You can also use a nested for loop like shown below
n = 3
arr = []
for x in range(n):
arr.append([])
for y in range(n):
arr[x].append([])
for z in range(n):
arr[x][y].append(0)
print(arr)

There are many ways to address your problem.
First one as accepted answer by #robert. Here is the generalised
solution for it:
def multi_dimensional_list(value, *args):
#args dimensions as many you like. EG: [*args = 4,3,2 => x=4, y=3, z=2]
#value can only be of immutable type. So, don't pass a list here. Acceptable value = 0, -1, 'X', etc.
if len(args) > 1:
return [ multi_dimensional_list(value, *args[1:]) for col in range(args[0])]
elif len(args) == 1: #base case of recursion
return [ value for col in range(args[0])]
else: #edge case when no values of dimensions is specified.
return None
Eg:
>>> multi_dimensional_list(-1, 3, 4) #2D list
[[-1, -1, -1, -1], [-1, -1, -1, -1], [-1, -1, -1, -1]]
>>> multi_dimensional_list(-1, 4, 3, 2) #3D list
[[[-1, -1], [-1, -1], [-1, -1]], [[-1, -1], [-1, -1], [-1, -1]], [[-1, -1], [-1, -1], [-1, -1]], [[-1, -1], [-1, -1], [-1, -1]]]
>>> multi_dimensional_list(-1, 2, 3, 2, 2 ) #4D list
[[[[-1, -1], [-1, -1]], [[-1, -1], [-1, -1]], [[-1, -1], [-1, -1]]], [[[-1, -1], [-1, -1]], [[-1, -1], [-1, -1]], [[-1, -1], [-1, -1]]]]
P.S If you are keen to do validation for correct values for args i.e. only natural numbers, then you can write a wrapper function before calling this function.
Secondly, any multidimensional dimensional array can be written as single dimension array. This means you don't need a multidimensional array. Here are the function for indexes conversion:
def convert_single_to_multi(value, max_dim):
dim_count = len(max_dim)
values = [0]*dim_count
for i in range(dim_count-1, -1, -1): #reverse iteration
values[i] = value%max_dim[i]
value /= max_dim[i]
return values
def convert_multi_to_single(values, max_dim):
dim_count = len(max_dim)
value = 0
length_of_dimension = 1
for i in range(dim_count-1, -1, -1): #reverse iteration
value += values[i]*length_of_dimension
length_of_dimension *= max_dim[i]
return value
Since, these functions are inverse of each other, here is the output:
>>> convert_single_to_multi(convert_multi_to_single([1,4,6,7],[23,45,32,14]),[23,45,32,14])
[1, 4, 6, 7]
>>> convert_multi_to_single(convert_single_to_multi(21343,[23,45,32,14]),[23,45,32,14])
21343
If you are concerned about performance issues then you can use some libraries like pandas, numpy, etc.

n1=np.arange(90).reshape((3,3,-1))
print(n1)
print(n1.shape)

I just want notice that
distance = [[[0 for k in range(n)] for j in range(n)] for i in range(n)]
can be shortened to
distance = [[[0] * n for j in range(n)] for i in range(n)]

def n_arr(n, default=0, size=1):
if n is 0:
return default
return [n_arr(n-1, default, size) for _ in range(size)]
arr = n_arr(3, 42, 3)
assert arr[2][2][2], 42

If you insist on everything initializing as empty, you need an extra set of brackets on the inside ([[]] instead of [], since this is "a list containing 1 empty list to be duplicated" as opposed to "a list containing nothing to duplicate"):
distance=[[[[]]*n]*n]*n

Related

is it possible to generate a list in list for desired numbers?

I wanted to generate a list in list :
now, I have basically two options, either I input the list through a text file or I should generate the list by itself.
is it possible to generate this type of list by itself using nested loops?
[[0,0,0], [0,0,0] ,[0,0,0], [0,0,0,], [0,0,0]]
I wanted to put -1 at the place of the middle zero of each sub-list like [0, -1, 0], there are 5 sub-list so the -1 should be inserted 5 times
so desired result would be
[[0,-1,0], [0,0,0] ,[0,0,0], [0,0,0,], [0,0,0]]
[[0,0,0], [0,-1,0] ,[0,0,0], [0,0,0,], [0,0,0]]
[[0,0,0], [0,0,0] ,[0,-1,0], [0,0,0,], [0,0,0]]
[[0,0,0], [0,0,0] ,[0,0,0], [0,-1,0,], [0,0,0]]
[[0,0,0], [0,0,0] ,[0,0,0], [0,0,0,], [0,-1,0]]
In my actual work, I have 38 sub-lists, for convenience, I showed only 5 here.
my attempt -
presently I am doing this by using json.loads and inputting this as a dictionary then collecting it using append and further converting it into a list and then I'll use those values.
however, this method seems so cumbersome to me.
F = []
import json
with open('unitvalue.txt') as f:
f_1 = {int(key): json.loads(val) for key, val in json.loads(f.readline()).items()}
f_2 = {int(key): json.loads(val) for key, val in json.loads(f.readline()).items()}
f_3 = {int(key): json.loads(val) for key, val in json.loads(f.readline()).items()}
f_4 = {int(key): json.loads(val) for key, val in json.loads(f.readline()).items()}
f_5 = {int(key): json.loads(val) for key, val in json.loads(f.readline()).items()}
where unitvalue.txt contain
{"1":"[0,-1,0]", "2":"[0,0,0]","3":"[0,0,0]", "4":"[0,0,0]", "5":"[0,0,0]"}
{"1":"[0,0,0]", "2":"[0,-1,0]","3":"[0,0,0]", "4":"[0,0,0]", "5":"[0,0,0]"}
{"1":"[0,0,0]", "2":"[0,0,0]","3":"[0,-1,0]", "4":"[0,0,0]", "5":"[0,0,0]"}
{"1":"[0,0,0]", "2":"[0,0,0]","3":"[0,0,0]", "4":"[0,-1,0]", "5":"[0,0,0]"}
{"1":"[0,0,0]", "2":"[0,0,0]","3":"[0,0,0]", "4":"[0,0,0]", "5":"[0,-1,0]"}
You can do it with a list comprehension:
n = 5
[[[0, -1, 0] if i == j else [0, 0, 0] for j in range(n)] for i in range(n)]
Output:
[[[0, -1, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, -1, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, -1, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, -1, 0], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, -1, 0]]]

Numpy assignment of 3D array values using 2D index array

I have a 3D numpy array of zeros, with dimensions CxHxW (in this example, C=4, H=2, and W=3):
A = np.array([[[0, 0, 0],
[0, 0, 0]],
[[0, 0, 0],
[0, 0, 0]]
[[0, 0, 0],
[0, 0, 0]]
[[0, 0, 0],
[0, 0, 0]]])
I also have a 2D array of indices, with dimensions HxW, such that every value in the array is a valid index between [0, C-1]
B = np.array([[2, 3, 0],
[3, 1, 2]])
Is there a fast way, using vectorization, to modify array A such that A[B[i][j]][i][j] = 1, for all valid i, j?
A = np.array([[[0, 0, 1],
[0, 0, 0]],
[[0, 0, 0],
[0, 1, 0]]
[[1, 0, 0],
[0, 0, 1]]
[[0, 1, 0],
[1, 0, 0]]])
Thank you!
It seems like you are looking for put_along_axis:
np.put_along_axis(A, B[None,...], 1, 0)
Note that the second argument is required to have the same number of dimensions as the first, which is why B[None,...] is used instead of B.

Python - Move Elements to the End of a 2-Dimensional Array?

Given this 2D Array - [[0, 0, -1], [1, 0, -2], [2, 0, -3], [3, 0, 0]]
How can I code something in Python that moves everything to the end? (If the end is empty).
So I get something like this - [[0, 0, 0], [1, 0, -1], [2, 0, -2], [3, 0, -3]]
I tried something like this:
count = 0
while count < row: # row is 4 in this case.
if my_array[row-1][2] == 0:
tp = my_array[count][2]
my_array[count][2] = 0
my_array[count+1][2] = tp
else:
break
count += 1
return my_array
But this is what I get instead:
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, -1]]
Update:
Thanks #furas, I spent hours trying to figure this out
How can I also handle a gap in the middle? So if I get something like this:
data = [[0, 0, -1], [0, 0, 0], [0, 0, -3], [0, 0, -4]]
expected = [[0, 0, 0], [0, 0, -1], [0, 0, -3], [0, 0, -4]]
rows = len(data)
if data[-1][-1] == 0:
count = rows-1
while count > 0:
data[count][-1] = data[count-1][-1]
count -= 1
data[0][-1] = 0
print(data)
print(expected)
print(data == expected) # False
Thanks
UPDATE Nov.29 -
I have certain scenarios like these that don't work? Can you help?
DATA: [[0, 0, 0, 0], [1, 2, 1, 0], [2, 1, 2, 0], [0, 0, 0, 0]]
EXPECT: [[0, 0, 0, 0], [0, 0, 0, 0], [1, 2, 1, 0], [2, 1, 2, 0]]
Or if it's negative:
DATA: [[0, 0, 0, 0], [-101, -101, -101, 0], [2, 3, 2, 0], [3, 2, 3, 0]]
EXPECT: [[0, 0, 0, 0], [0, 0, 0, 0], [2, 3, 2, 0], [3, 2, 3, 0]]
Thanks A lot!!
I got something like this
def move(data):
row = len(data)-1
prev = row-1
while row > 0 and prev >= 0:
if data[row][-1] == 0:
while prev >= 0 and data[prev][-1] == 0:
prev -= 1
data[row][-1] = data[prev][-1]
data[prev][-1] = 0
row -= 1
prev -= 1
return data
# --- test ---
examples = [
{
'data': [[0, 0, -1], [0, 0, 0], [0, 0, -3], [0, 0, -4]],
'expected': [[0, 0, 0], [0, 0, -1], [0, 0, -3], [0, 0, -4]],
},
{
'data': [[0, 0, -1], [1, 0, -2], [2, 0, -3], [3, 0, 0]],
'expected': [[0, 0, 0], [1, 0, -1], [2, 0, -2], [3, 0, -3]],
},
]
for ex in examples:
print(ex['data'])
result = move(ex['data'])
print(ex['expected'])
print(result == ex['expected']) # True
I use rows = len(data) and [-1] so it can have any size.
External while check if place row is empty and start internal while which is looking for not-empty elements prev before row. After that it move non-empty element from prev to row and put zero in prev. And it starts again with next row place.
It looks more complicated then other solutions.
Here's a reasonably efficient way to do this. We grab the last column of the grid into a list named lastcol, and then sort lastcol with a key function that tests if each item is not equal to zero. That will move all the zeros to the start of lastcol without disturbing the order of the other elements. And then we copy lastcol back to the grid.
I've modified your test data slightly so that we can see that the code handles gaps and positive & negative values correctly.
grid = [[0, 0, -1], [1, 0, 2], [2, 0, 0], [3, 0, -3], [4, 0, 0]]
print(grid)
lastcol = [u[-1] for u in grid]
lastcol.sort(key=(0).__ne__)
for row, u in zip(grid, lastcol):
row[-1] = u
print(grid)
output
[[0, 0, -1], [1, 0, 2], [2, 0, 0], [3, 0, -3], [4, 0, 0]]
[[0, 0, 0], [1, 0, 0], [2, 0, -1], [3, 0, 2], [4, 0, -3]]
Assuming I'm understanding the goal correctly -- that you'd want a column [0, 1, 0, 2, 3] to become [0, 0, 1, 2, 3] -- then with the use of a helper function to transpose the array, all we need to do is sort the columns using bool as the key function, because non-zero numbers are false, and False < True.
def transpose(seq):
return [list(part) for part in zip(*seq)]
def push(seq):
return transpose(sorted(part, key=bool) for part in transpose(seq))
which gives me
In [29]: push([[0, 0, -1], [1, 0, -2], [2, 0, -3], [3, 0, 0]])
Out[29]: [[0, 0, 0], [1, 0, -1], [2, 0, -2], [3, 0, -3]]
In [30]: push([[0, 0, -1], [0, 0, 0], [0, 0, -3], [0, 0, -4]])
Out[30]: [[0, 0, 0], [0, 0, -1], [0, 0, -3], [0, 0, -4]]

One-Hot Encoding without for-loop from vector of positions in Python with NumPy?

I have some data that I want to "one-hot encode" and it is represented as a 1-dimensional vector of positions.
Is there any function in NumPy that can expand my x into my x_ohe?
I'm trying to avoid using for-loops in Python at all costs for operations like this after watching Jake Vanderplas's talk
x = np.asarray([0,0,1,0,2])
x_ohe = np.zeros((len(x), 3), dtype=int)
for i, pos in enumerate(x):
x_ohe[i,pos] = 1
x_ohe
# array([[1, 0, 0],
# [1, 0, 0],
# [0, 1, 0],
# [1, 0, 0],
# [0, 0, 1]])
If x only contains non negative integers, you can compare x with a sequence use numpy broadcasting and convert the result to ints:
(x[:,None] == np.arange(x.max()+1)).astype(int)
#array([[1, 0, 0],
# [1, 0, 0],
# [0, 1, 0],
# [1, 0, 0],
# [0, 0, 1]])
Or initialize first, then assign ones use advanced indexing:
x_ohe = np.zeros((len(x), 3), dtype=int)
x_ohe[np.arange(len(x)), x] = 1
x_ohe
#array([[1, 0, 0],
# [1, 0, 0],
# [0, 1, 0],
# [1, 0, 0],
# [0, 0, 1]])
A one liner :
np.equal.outer(x,range(3)).astype(int)
array([[1, 0, 0],
[1, 0, 0],
[0, 1, 0],
[1, 0, 0],
[0, 0, 1]])
np.equal.outer(x,np.unique(x)).astype(int) works also here.

Python - Move Elements in a 2-Dimensional Array?

Given this 2D Array -
[[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]
How can I code something in Python that shifts once to the end each time it's called? However, it has to stop if it reaches a positive value.
So I get something like this
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]
This is what I have. It works for condition 1 but it does not work for condition 2. What can I tweak so condition 2 also works?
# Condition 1 - Works
data = [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]]
expected = [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]] # My Results.
# Condition 2 - Does Not Work
data = [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]
expected = [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]] # This is what I want to get. (But it doesn't work)
rows = len(data)
if data[-1][-1] == 0:
count = rows-1
while count > 0:
data[count][-1] = data[count-1][-1]
count -= 1
data[0][-1] = 0
print(data)
print(expected)
This is what I am getting currently. I want to get the expected for Condition 2 listed in the code snipped above.:
Condition 2 Result:
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
Thanks
Update #Furas:
Something like this to find the positive value location?
possitive_value = []
for i in range(len(data)):
if data[i][-1] > 0:
possitive_value.append(i, -1)
I think you have to find first positive value and use its position as rows
EDIT: I changed name count to last to make it more readable
# --- function ---
def move(data):
rows = len(data)
# - find positive -
for x in range(rows):
if data[x][-1] > 0:
rows = x # use its position as `rows`
break # don't seach other positiove values
# - star moving -
# set "last" checked row
last = rows-1
# check "last" row
if data[last][-1] == 0:
# move previous values
while last > 0:
data[last][-1] = data[last-1][-1]
last -= 1
# put 0 in first place
data[0][-1] = 0
# --- tests ---
examples = [
{
# Condition 1 - Works
'data': [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]],
'expected': [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]], # My Results.
},
{
# Condition 2 - Works
'data': [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]],
'expected': [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]], # This is what I want to get. (But it doesn't work)
}
]
for example in examples:
data = example['data']
expected = example['expected']
print(' before:', data)
move(data)
print(' after:', data)
print('expected:', expected)
print(' correct:', data == expected)
print('---')
Result:
before: [[0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0], [0, 0, 0]]
after: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]]
expected: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, -3], [0, 0, 0]]
correct: True
---
before: [[0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3], [0, 0, 0]]
after: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]
expected: [[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]
correct: True
---
EDIT:
BTW: instead of while you can use for with reversed range() if it makes it more readable
def move(data):
rows = len(data)
# - find positive -
for x in range(rows):
if data[x][-1] > 0:
rows = x # use its position as `rows`
break # don't seach other positiove values
# - star moving -
# set "last" checked row
last = rows-1
# check "last" row
if data[last][-1] == 0:
# move previous values
for pos in range(last, 0, -1): # range with reversed order
data[pos][-1] = data[pos-1][-1]
# put 0 in first place
data[0][-1] = 0
BTW: both versions move items in original data ("in-place") so they don't need return data.
Here's a variation, a function that can be called and will shift data by one every time. I wasn't entirely clear from your question if this was the behavior you wanted.
import numpy as np
def shift_data(data):
ele_idx = [i for i,x in enumerate(data) if x != [0,0,0]]
zero_idx = [i for i,x in enumerate(data) if x == [0,0,0] and i != 0]
if max(np.diff(ele_idx)) == 1 or max(np.diff([i for i,x in enumerate(data) if x == [0,0,0]])) == 1:#things are consecutive
data.insert(0,data.pop(-1))
else:
tt = [l for l in data[ele_idx[0]:zero_idx[0]+1]]
tt.insert(0,tt.pop(-1))
data = data[:ele_idx[0]] + tt + data[zero_idx[0]+1:]
return data
data = [[0, 0, -1], [0, 0, 0], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
print(data)
for _ in range(0,6):
data = shift_data(data)
print(data)
This code outputs:
[[0, 0, -1], [0, 0, 0], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 0], [0, 0, 3]]
[[0, 0, 0], [0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3]]
[[0, 0, 3], [0, 0, 0], [0, 0, 0], [0, 0, -1], [0, 0, -2]]
[[0, 0, -2], [0, 0, 3], [0, 0, 0], [0, 0, 0], [0, 0, -1]]
[[0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, -1], [0, 0, -2], [0, 0, 3], [0, 0, 0]]

Categories

Resources