Implementing a nested or multidimensional python list extended slicing - python

I want to implement a multidimentional list slicing. I call it part like similar built-in function of Mathematica's Part.
What I got so far is below definition.
class slicee:
def __getitem__(self, item):
return item
def part(x,sliceList):
if len(sliceList)==1:
return x[sliceList[0]]
else:
if type(sliceList[0])==slice:
tmp=x[sliceList[0]]
else:
tmp=[x[sliceList[0]]]
return [part(i,sliceList[1:]) for i in tmp]
Now we use this part. Define a nested list(just for convinience, I use all numbers, which I know numpy can slice it perfectly. But it just serves an example.)
l=[[[8, 9, 5, 5], [2, 0, 3, 4], [1, 1, 6, 6], [3, 9, 9, 3]],
[[3, 4, 0, 2], [3, 1, 0, 2], [8, 2, 9, 5], [3, 5, 6, 8]],
[[3, 5, 7, 2], [7, 2, 3, 9], [2, 1, 5, 2], [7, 6, 2, 2]],
[[3, 0, 8, 3], [3, 7, 8, 1], [9, 4, 7, 2], [2, 0, 5, 7]]]
and
part(l,slicee()[:,:,0])
gives
[[8, 2, 1, 3], [3, 3, 8, 3], [3, 7, 2, 7], [3, 3, 9, 2]]
This is quite good except that I don't like the interface which have to explicitly write slicee()
Is it possible to make the interface of part to be part(l,[:,:,0]) or even better part(l,:,:,0)? Or is there already some package deal with this general slicing?

Enlightened by wwii, I made below part class which fix some bugs in both wwii's answer and my post
class part(list):
def __getitem__(self, item):
b=list(self)
def part_internal(x,slice_spec):
if type(slice_spec)==tuple:
if len(slice_spec)>1:
tmp=x[slice_spec[0]]
if type(slice_spec[0])==slice:
return [part_internal(i,slice_spec[1:]) for i in tmp]
else:
return part_internal(tmp,slice_spec[1:])
else:
return x[slice_spec[0]]
else:
return x[slice_spec]
return part_internal(b,item)
Define
l=[[[8, 9, 5, 5], [2, 0, 3, 4], [1, 1, 6, 6], [3, 9, 9, 3]],
[[3, 4, 0, 2], [3, 1, 0, 2], [8, 2, 9, 5], [3, 5, 6, 8]],
[[3, 5, 7, 2], [7, 2, 3, 9], [2, 1, 5, 2], [7, 6, 2, 2]],
[[3, 0, 8, 3], [3, 7, 8, 1], [9, 4, 7, 2], [2, 0, 5, 7]]]
Now we have
print(part(l)[0])
print(part(l)[0,0])
print(part(l)[0,0,0])
print(part(l)[:,0])
print(part(l)[:,:,0])
print(part(l)[:,0:2,0:2])
gives
[[8, 9, 5, 5], [2, 0, 3, 4], [1, 1, 6, 6], [3, 9, 9, 3]]
[8, 9, 5, 5]
8
[[8, 9, 5, 5], [3, 4, 0, 2], [3, 5, 7, 2], [3, 0, 8, 3]]
[[8, 2, 1, 3], [3, 3, 8, 3], [3, 7, 2, 7], [3, 3, 9, 2]]
[[[8, 9], [2, 0]], [[3, 4], [3, 1]], [[3, 5], [7, 2]], [[3, 0], [3, 7]]]

This is the closest I could get. Subclass list and override __getitem__.
class Foo(list):
def __getitem__(self, item):
b = list(self)
for thing in item:
b = b[thing]
return b
a = [[[8, 9, 5, 5], [2, 0, 3, 4], [1, 1, 6, 6], [3, 9, 9, 3]],
[[3, 4, 0, 2], [3, 1, 0, 2], [8, 2, 9, 5], [3, 5, 6, 8]],
[[3, 5, 7, 2], [7, 2, 3, 9], [2, 1, 5, 2], [7, 6, 2, 2]],
[[3, 0, 8, 3], [3, 7, 8, 1], [9, 4, 7, 2], [2, 0, 5, 7]]]
>>> a = Foo(a)
>>> a[:,:,1]
[[3, 4, 0, 2], [3, 1, 0, 2], [8, 2, 9, 5], [3, 5, 6, 8]]
>>> a[:,1,1]
[3, 1, 0, 2]
>>> a[1,1,1]
1
>>>
Caveat: Did not assess unintended consequences. Here are a couple...
>>> a[1,]
[[3, 4, 0, 2], [3, 1, 0, 2], [8, 2, 9, 5], [3, 5, 6, 8]]
>>> a[1]
Traceback (most recent call last):
File "<pyshell#458>", line 1, in <module>
a[1]
File "C:\pyProjects\tmp.py", line 34, in __getitem__
for thing in item:
TypeError: 'int' object is not iterable
>>> a[1,1,1,1]
Traceback (most recent call last):
File "<pyshell#457>", line 1, in <module>
a[1,1,1,1]
File "C:\pyProjects\tmp.py", line 35, in __getitem__
b = b[thing]
TypeError: 'int' object is not subscriptable
In order to implement as a function with two arguments - part(thelist,theslice). You would need to parse theslice argument and expand it to a tuple of slice objects.

Related

Getting error in function for iterating list of lists

I am writing a function to pad a list:
def pad_data(game_data,nofrounds):
for i in game_data:
c=len(i)
if (c<nofrounds):
r=nofrounds-len(i)
value=i[-1]
for k in range(0,r):
game_data.append(value)
I am getting the following error despite trying numerous times:
TypeError: object of type 'int' has no len()
Whereas when I attempt to do it this way it gives me result:
game_data =[[1,2,3,4,6],[1,2,3],[5,6,7,8],[4,5,6,56,0]]
for i in game_data:
print(len(i))
5
3
4
5
While appending a value, your code is adding an integer in the game_data list. game_data grows like the following as the function runs:
[[1, 2, 3, 4, 6], [1, 2, 3], [5, 6, 7, 8], [4, 5, 6, 56, 0]]
[[1, 2, 3, 4, 6], [1, 2, 3], [5, 6, 7, 8], [4, 5, 6, 56, 0]]
[[1, 2, 3, 4, 6], [1, 2, 3], [5, 6, 7, 8], [4, 5, 6, 56, 0], 3, 3]
[[1, 2, 3, 4, 6], [1, 2, 3], [5, 6, 7, 8], [4, 5, 6, 56, 0], 3, 3, 8]
[[1, 2, 3, 4, 6], [1, 2, 3], [5, 6, 7, 8], [4, 5, 6, 56, 0], 3, 3, 8]
Notice how initial list is now storing a list of both list and integers instead of just list only.
len(3) will then give you the TypeError.

Adding a n extra colum from another matrix

I have these two matrices:
A = [[2, 7, 3, 6], [3, 3, 4, 4], [6, 9, 5, 3], [4, 2, 1, 7]]
B = [[2, 6, 3, 5], [-1, 2, -3, 1], [2, -5, 7, 3]]
I need to create three matrices A+1st column of B, and so on
So I need this final result:
A1 = [[2, 7, 3, 6, 2], [3, 3, 4, 4, 6], [6, 9, 5, 3, 3], [4, 2, 1, 7, 5]]
A2 = [[2, 7, 3, 6, -1], [3, 3, 4, 4, 2], [6, 9, 5, 3, -3], [4, 2, 1, 7, 7]]
A3 = [[2, 7, 3, 6, 2], [3, 3, 4, 4, -5], [6, 9, 5, 3, 7], [4, 2, 1, 7, 3]]
I start doing the following code, but I only got one of those
for j in range(len(B)):
for i in range(j):
b = B[j][i]
A = [x + [b] for x in A]
print(A)
Output:
[[2, 7, 3, 6], [3, 3, 4, 4], [6, 9, 5, 3], [4, 2, 1, 7]]
[[2, 7, 3, 6, -1], [3, 3, 4, 4, -1], [6, 9, 5, 3, -1], [4, 2, 1, 7, -1]]
[[2, 7, 3, 6, -1, 2, -5], [3, 3, 4, 4, -1, 2, -5], [6, 9, 5, 3, -1, 2, -5], [4, 2, 1, 7, -1, 2, -5]]
Process finished with exit code 0
You can leverage enumerate() to get your output:
A = [[2, 7, 3, 6], [3, 3, 4, 4], [6, 9, 5, 3], [4, 2, 1, 7]]
B = [[2, 6, 3, 5], [-1, 2, -3, 1], [2, -5, 7, 3]]
AA ={}
for idx,inner in enumerate(B):
# add to each a from A the k-th elem of your inner
AA[f"A{idx}"] = [a +[inner[k]] for k,a in enumerate(A)]
print(AA) # stored the lists into a dict
{'A0': [[2, 7, 3, 6, 2], [3, 3, 4, 4, 6], [6, 9, 5, 3, 3], [4, 2, 1, 7, 5]],
'A1': [[2, 7, 3, 6, -1], [3, 3, 4, 4, 2], [6, 9, 5, 3, -3], [4, 2, 1, 7, 1]],
'A2': [[2, 7, 3, 6, 2], [3, 3, 4, 4, -5], [6, 9, 5, 3, 7], [4, 2, 1, 7, 3]]}
As a personal preference I avoid using range(len(...)) where I can - enumerate is cleaner.
If string literals do not work for you, use "A{}".format(idx) instead.
Your code does not work because you coupled the range of i to j
for j in range(len(B)): # j starts as 0,1,2
for i in range(j): # i is doing nothing, 0, 0+1 <-- wrong
This is a possible solution that you can certainly start with:
k = len(A)
D = []
for i in range(len(B)):
D.append([])
for j in range(len(A)):
C = A[j][:k]
C.append(B[i][j])
D[i].append(C)
Make sure to use deepcopy so that they are not all referring to the same object.
A = [[2, 7, 3, 6], [3, 3, 4, 4], [6, 9, 5, 3], [4, 2, 1, 7]]
B = [[2, 6, 3, 5], [-1, 2, -3, 1], [2, -5, 7, 3]]
from copy import deepcopy
A_list = [deepcopy(A) for i in range(3)]
for outer_idx, list_in_b in enumerate(B):
for inner_idx, value in enumerate(list_in_b):
A_list[outer_idx][inner_idx].append(value)
print(A_list)
# Output:
#[[[2, 7, 3, 6, 2], [3, 3, 4, 4, 6], [6, 9, 5, 3, 3], [4, 2, 1, 7, 5]],
# [[2, 7, 3, 6, -1], [3, 3, 4, 4, 2], [6, 9, 5, 3, -3], [4, 2, 1, 7, 1]],
# [[2, 7, 3, 6, 2], [3, 3, 4, 4, -5], [6, 9, 5, 3, 7], [4, 2, 1, 7, 3]]]

Iterate over slices of n elements to the left and right of each item in a list in python

Suppose I have the following list:
>>> my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to write a function that will iterate over slices of n elements to the left and right of each item of this list:
def sliding_window_n_elements_left_and_right(lst, n):
...
The function should work like this:
>>> list(sliding_window_n_elements_left_and_right(my_list, 0))
[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
>>> list(sliding_window_n_elements_left_and_right(my_list, 1))
[[0, 1], [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10], [9, 10]]
>>> list(sliding_window_n_elements_left_and_right(my_list, 2))
[[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, 10], [7, 8, 9, 10], [8, 9, 10]]
Thank you for your time and skill!
You can slice the list at each index i padding with n on left and right to create a fixed window of max size 2n+1:
def sliding_window_n_elements_left_and_right(lst, n):
# start = max(0, i-n) prevents slice from starting at negative value
return [lst[max(0, i-n):i+n+1] for i in range(len(lst))]
print(sliding_window_n_elements_left_and_right(my_list, 0))
# [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
print(sliding_window_n_elements_left_and_right(my_list, 1))
# [[0, 1], [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10], [9, 10]]
print(sliding_window_n_elements_left_and_right(my_list, 2))
# [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7], [4, 5, 6, 7, 8], [5, 6, 7, 8, 9], [6, 7, 8, 9, 10], [7, 8, 9, 10], [8, 9, 10]]

[Python]: generate and array of all possible combinations

I have a very straightforward combination problem. I have two arrays (a and b). Array a indicates all the values one of the three slots in array b can take on. Each slot in array b can have a value between 1 and 5. An example of which would be [1, 4, 5]. I would like to generate an array (c) with all possible combinations. I like to extend the basic example larger arrays.
Input:
a = [1, 2, 3, 4, 5]
b = [1, 2, 3]
Output:
c = [[1, 1, 1], [1, 1, 2],[1, 1, 3], [1, 1, 4], [1, 1, 5],
[1, 2, 1], [1, 2, 2],[1, 2, 3], [1, 2, 4], [1, 2, 5],
[1, 3, 1], [1, 3, 2],[1, 3, 3], [1, 3, 4], [1, 3, 5],
[1, 4, 1], [1, 4, 2],[1, 4, 3], [1, 4, 4], [1, 4, 5],
[1, 5, 1], [1, 5, 2],[1, 5, 3], [1, 5, 4], [1, 5, 5],
[2, 1, 1], [2, 1, 2],[2, 1, 3], [2, 1, 4], [2, 1, 5],
[2, 2, 1], [2, 2, 2],[2, 2, 3], [2, 2, 4], [2, 2, 5],
[2, 3, 1], [2, 3, 2],[2, 3, 3], [2, 3, 4], [2, 3, 5],
[2, 4, 1], [2, 4, 2],[2, 4, 3], [2, 4, 4], [2, 4, 5],
[2, 5, 1], [2, 5, 2],[2, 5, 3], [2, 5, 4], [2, 5, 5],
[3, 1, 1], [3, 1, 2],[3, 1, 3], [3, 1, 4], [3, 1, 5],
[3, 2, 1], [3, 2, 2],[3, 2, 3], [3, 2, 4], [3, 2, 5],
[3, 3, 1], [3, 3, 2],[3, 3, 3], [3, 3, 4], [3, 3, 5],
[3, 4, 1], [3, 4, 2],[3, 4, 3], [3, 4, 4], [3, 4, 5],
[3, 5, 1], [3, 5, 2],[3, 5, 3], [3, 5, 4], [3, 5, 5],
[4, 1, 1], [4, 1, 2],[4, 1, 3], [4, 1, 4], [4, 1, 5],
[4, 2, 1], [4, 2, 2],[4, 2, 3], [4, 2, 4], [4, 2, 5],
[4, 3, 1], [4, 3, 2],[4, 3, 3], [4, 3, 4], [4, 3, 5],
[4, 4, 1], [4, 4, 2],[4, 4, 3], [4, 4, 4], [4, 4, 5],
[5, 5, 1], [5, 5, 2],[5, 5, 3], [5, 5, 4], [5, 5, 5],
[5, 1, 1], [5, 1, 2],[5, 1, 3], [5, 1, 4], [5, 1, 5],
[5, 2, 1], [5, 2, 2],[5, 2, 3], [5, 2, 4], [5, 2, 5],
[5, 3, 1], [5, 3, 2],[5, 3, 3], [5, 3, 4], [5, 3, 5],
[5, 4, 1], [5, 4, 2],[5, 4, 3], [5, 4, 4], [5, 4, 5],
[5, 5, 1], [5, 5, 2],[5, 5, 3], [5, 5, 4], [5, 5, 5]]
Solution for the problem above:
d = []
for i in range(len(a)):
for j in range(len(a)):
for k in range(len(a)):
e = []
e.append(i+1)
e.append(j+1)
e.append(k+1)
d.append(e)
I am looking for a more generic way. One which can accommodate larger arrays (see below) without the need to use a nested for loop structure. I searched for a comparable example, but was unable to find one on stackoverflow.
Input:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
You are looking for itertools.product().
a = [1, 2, 3, 4, 5]
b = 3 # Actually, you just need the length of the array, values do not matter
c = itertools.product(a, repeat=b)
Note that this returns an iterator, you may need to cast it using list() but be aware this can take forever and highly consume memory if the sizes grow.
In the general case, you should of course use the itertools module, in this particular case itertools.product, as explained in the other answer.
If you want to implement the function yourself, you can use recursion to make it applicable to any array sizes. Also, you should probably make it a generator function (using yield instead of return), as the result could be rather long. You can try something like this:
def combinations(lst, num):
if num > 0:
for x in lst:
for comb in combinations(lst, num - 1):
yield [x] + comb
else:
yield []

Unpack items and add a new one

I need to create a new list merging two lists where one of them is a list of lists. Here's what I need to do:
a = [[2, 1, 4, 5, 0], [3, 6, 5, 4, 8], [2, 1, 4, 7, 8], [3, 4, 9, 5, 6], [7, 5, 2, 1, 1]]
b = [2, 3, 5, 0, 8]
c = []
for indx, item in enumerate(a):
c.append([item, b[indx]])
This generates c as:
[[[2, 1, 4, 5, 0], 2], [[3, 6, 5, 4, 8], 3], [[2, 1, 4, 7, 8], 5], [[3, 4, 9, 5, 6], 0], [[7, 5, 2, 1, 1], 8]]
but I would need it to look like:
[[2, 1, 4, 5, 0, 2], [3, 6, 5, 4, 8, 3], [2, 1, 4, 7, 8, 5], [3, 4, 9, 5, 6, 0], [7, 5, 2, 1, 1], 8]
I've tried adding a * in front of item to unpack the elements but that doesn't work.
Just concatenate the items to create a new list from item and the element from b:
for indx, item in enumerate(a):
c.append(item + [b[indx]])
You can simplify your loop using the zip() function:
for a_item, b_item in zip(a, b):
c.append(a_item + [b_item])
Then move the whole definition of c to a list comprehension:
c = [a_item + [b_item] for a_item, b_item in zip(a, b)]
Demo:
>>> a = [[2, 1, 4, 5, 0], [3, 6, 5, 4, 8], [2, 1, 4, 7, 8], [3, 4, 9, 5, 6], [7, 5, 2, 1, 1]]
>>> b = [2, 3, 5, 0, 8]
>>> [a_item + [b_item] for a_item, b_item in zip(a, b)]
[[2, 1, 4, 5, 0, 2], [3, 6, 5, 4, 8, 3], [2, 1, 4, 7, 8, 5], [3, 4, 9, 5, 6, 0], [7, 5, 2, 1, 1, 8]]
You can make use of zip() function and list comprehension:
>>> a = [[2, 1, 4, 5, 0], [3, 6, 5, 4, 8], [2, 1, 4, 7, 8], [3, 4, 9, 5, 6], [7, 5, 2, 1, 1]]
>>> b = [2, 3, 5, 0, 8]
>>> [elem1 + [elem2] for elem1, elem2 in zip(a, b)]
[[2, 1, 4, 5, 0, 2], [3, 6, 5, 4, 8, 3], [2, 1, 4, 7, 8, 5], [3, 4, 9, 5, 6, 0], [7, 5, 2, 1, 1, 8]]

Categories