Pythonic way to generate a list of possible arrays - python

Given an array of numbers (e.g. [3, 5, 2]), I'm trying to generate a list of possible arrays that result from adding 1 to one entry in the array: [[4, 5, 2], [3, 6, 2], [3, 5, 3]].
I can get it done by the following, but wondering if there's a more pythonic way to get the result?
test = [3, 5, 2]
result = [t.copy() for _ in range(len(test))]
for index, _ in enumerate(result):
result[index][index] += 1

Here's how to do it with a list comprehension:
test = [3, 5, 2]
print [test[:i] + [v + 1] + test[i+1:] for i,v in enumerate(test)]
output
[[4, 5, 2], [3, 6, 2], [3, 5, 3]]

Here is another inline solution with list comprehension:
test = [3, 5, 2]
result = [[v+1 if i==j else v for j, v in enumerate(test)] for i in range(len(test))]
or, as noticed by PM 2Ring, you can exploit the fact that True == 1 and False == 0:
result = [[v + (i==j) for j, v in enumerate(test)] for i in range(len(test))]

Related

Iterator in not generating list correctly

Why this code is returning [[2, 3]] instead of [[1, 2, 3], [2, 4, 6], [3, 6, 9]] ?
i = (i for i in range(1, 4))
l = [[x * y for y in i] for x in i]
print(l)
i is generator object, which means the values in it are consumed after the first iteration. In [[x * y for y in i] for x in i] you iterate over it twice, in the seconed time its empty. You can change i to list instead
i = [i for i in range(1, 4)]
l = [[x * y for y in i] for x in i]
print(l)
# output [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
As already noted you created i which is exhaustable, I suggest different fix: just use range directly i.e:
i = range(1, 4)
l = [[x * y for y in i] for x in i]
print(l)
output:
[[1, 2, 3], [2, 4, 6], [3, 6, 9]]
call to range does create instance of class range which might be used multiple times for example
x = range(1,4)
print(list(x))
print(list(x))
print(list(x))
output:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]

How to change the index of a list?

I have two lists like below:
A = [[0, [1, 2]], [1, [3, 4]], [2, [5, 6]], [3, [7, 8]], [4, [9, 10]], [5, [11, 12]], [6, [13, 14]]]
and
B = [[0, [1, 2]], [1, [4, 5]], [4, [[7, 8], [9, 10]]]]
I want to replace some elements of A based on some conditions related to list B.
I have written a code that does what I'm looking for, as below:
x = 3
v = [0, 1, 4]
for i in range (x):
if i in v and B[i][0] == A[i][0]:
A[i][1][0] = B[i][1][1]
for elem in v:
if elem not in range(x):
A[elem][1][0] = B[2][1][1][0]
A[elem+1][1][0] = B[2][1][1][1]
else:
A = A
print (A)
My problem is with these lines:
for elem in v:
if elem not in range (x):
A[elem][1][0] = B[2][1][1][0]
A[elem+1][1][0] = B[2][1][1][1]
As you can see, after looking through the elements of list v, and check if those elements are not in range (x), in this case, that element is 4, I want to replace some elements of A with some elements of B in this case that element is [4, [[7, 8], [9, 10]]] , However, the index of this element in list B is 2. Is there any other way to use 4 in [4, [[7, 8], [9, 10]]] which is also an element of v inside the code instead of writing B[2]? I want to use [x[0] for x in B] as the indicators instead of using the indexes, as those are different.
Thanks
if you want to stick to the structure of your current code, you could use np.where for this
x= 3
v = [0, 1, 4]
import numpy as np
for i in range (x):
if i in v and B[i][0] == A[i][0]:
A [i][1][0] = B[i][1][1]
for elem in v:
if elem not in range (x):
# get index (in this case: 2)
ind = np.where(np.array([i[0] for i in B]) == elem)[0][0]
A [elem][1][0] = B[ind][1][1][0]
A [elem+1][1][0] = B[ind][1][1][1]
else:
A = A
print (A)

How to do Math Functions on Lists within a List

I'm very new to python (using python3) and I'm trying to add numbers from one list to another list. The only problem is that the second list is a list of lists. For example:
[[1, 2, 3], [4, 5, 6]]
What I want is to, say, add 1 to each item in the first list and 2 to each item in the second, returning something like this:
[[2, 3, 4], [6, 7, 8]]
I tried this:
original_lst = [[1, 2, 3], [4, 5, 6]]
trasposition_lst = [1, 2]
new_lst = [x+y for x,y in zip(original_lst, transposition_ls)]
print(new_lst)
When I do this, I get an error
can only concatenate list (not "int") to list
This leads me to believe that I can't operate in this way on the lists as long as they are nested within another list. I want to do this operation without flattening the nested list. Is there a solution?
One approach using enumerate
Demo:
l = [[1, 2, 3], [4, 5, 6]]
print( [[j+i for j in v] for i,v in enumerate(l, 1)] )
Output:
[[2, 3, 4], [6, 7, 8]]
You can use enumerate:
l = [[1, 2, 3], [4, 5, 6]]
new_l = [[c+i for c in a] for i, a in enumerate(l, 1)]
Output:
[[2, 3, 4], [6, 7, 8]]
Why don't use numpy instead?
import numpy as np
mat = np.array([[1, 2, 3], [4, 5, 6]])
mul = np.array([1,2])
m = np.ones(mat.shape)
res = (m.T *mul).T + mat
You were very close with you original method. Just fell one step short.
Small addition
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
new_lst = [[xx + y for xx in x] for x, y in zip(original_lst, transposition_lst)]
print(new_lst)
Output
[[2, 3, 4], [6, 7, 8]]
Reasoning
If you print your original zip it is easy to see the issue. Your original zip yielded this:
In:
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
for x,y in zip(original_lst, transposition_lst):
print(x, y)
Output
[1, 2, 3] 1
[4, 5, 6] 2
Now it is easy to see that you are trying to add an integer to a list (hence the error). Which python doesn't understand. if they were both integers it would add them or if they were both lists it would combine them.
To fix this you need to do one extra step with your code to add the integer to each value in the list. Hence the addition of the extra list comprehension in the solution above.
A different approach than numpy that could work even for lists of different lengths is
lst = [[1, 2, 3], [4, 5, 6, 7]]
c = [1, 2]
res = [[l + c[i] for l in lst[i]] for i in range(len(c))]

Longest increasing subsequence, algorithm works wrong, no idea why

I made an implementation of Longest Increasing Subsequence (LIS) algorithm, as I see it would work, but results are totally mess.
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))]
L[0].append(D[0])
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = L[j]
L[i].append(D[i])
print L
Returned result:
[[3], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [2, 6, 4, 5], [1]]
What it should be:
[[3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
As I saw in debugger when we have:
L[i] = L[j]
Not only L[i] gets new values, but other lists on the main (L) list too...
I don't know how to avoid it. It looks that lists in Python are totally different than vectors languages from C family...
I'm fighting with this for a long time. Huge beer to someone who gonna find what is wrong :(
When you state L[i] = L[j] you do not copy the content of the list, you simply copy a reference: from now on L[i] and L[j] point to the same list and changes made through L[i] will reflect when you obtain L[j].
A simply fix is simply to copy the list:
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))]
L[0].append(D[0])
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = list(L[j])
L[i].append(D[i])
print(L)
Now hoever your algorithm does not work anymore (it was not working in the first place nevertheless). When running your (fixed) code, you get:
>>> lis()
[[3, 3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
The 3 occurs twice in the first list, you can solve this by removing the .append before the for loop. So the final version is:
def lis():
#D = map(int, raw_input().split())
D = [3, 2, 6, 4, 5, 1]
L = [[] for i in range(len(D))] #removed the next line
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j]:
L[i] = list(L[j])
L[i].append(D[i])
print(L)
Which produces:
>>> lis()
[[3], [2], [2, 6], [2, 4], [2, 4, 5], [1]]
Note: based on your comment you use python-2.7, from python-3.x there is a method called .copy() on lists that you can call.
Important Note
The solution which is above is correct but if you try it out the result can be wrong for some instances.
For example - Longest Increasing Subsequence for numbers:
5 1 4 2 3 1 2 9 1
is
1 2 3 9
But this solution won't be on your L list returned by algorithm. There will be:
1 2 9
Max element from L list is needed. Then another element from D can be appended, so we have to add one more condition, here you go the correct code:
def lis():
#D = map(int, raw_input().split())
D = [5, 1, 4, 2, 3, 1, 2, 9, 1]
L = [[] for i in range(len(D))]
for i in range(len(D)):
for j in range(0,i):
if D[i] > D[j] and len(L[i]) < len(L[j])+1: #added condition
L[i] = list(L[j])
L[i].append(D[i])
print(L)
>>lis()
[[5], [1], [1, 4], [1, 2], [1, 2, 3], [1], [1, 2], [1, 2, 3, 9], [1]]

Python: split list of integers based on step between them

I have the following problem. Having a list of integers, I want to split it, into a list of lists, whenever the step between two elements of the original input list is not 1.
For example: input = [0, 1, 3, 5, 6, 7], output = [[0, 1], [3], [5, 6, 7]]
I wrote the following function, but it's uggly as hell, and I was wondering if anyone of you guys would help me get a nicer solution. I tried to use itertools, but couldn't solve it.
Here's my solution:
def _get_parts(list_of_indices):
lv = list_of_indices
tuples = zip(lv[:-1], lv[1:])
split_values = []
for i in tuples:
if i[1] - i[0] != 1:
split_values.append(i[1])
string = '/'.join([str(i) for i in lv])
substrings = []
for i in split_values:
part = string.split(str(i))
substrings.append(part[0])
string = string.lstrip(part[0])
substrings.append(string)
result = []
for i in substrings:
i = i.rstrip('/')
result.append([int(n) for n in i.split('/')])
return result
Thanks a lot!
This works with any iterable
>>> from itertools import groupby, count
>>> inp = [0, 1, 3, 5, 6, 7]
>>> [list(g) for k, g in groupby(inp, key=lambda i,j=count(): i-next(j))]
[[0, 1], [3], [5, 6, 7]]
def _get_parts(i, step=1):
o = []
for x in i:
if o and o[-1] and x - step == o[-1][-1]:
o[-1].append(x)
else:
o.append([x])
return o
_get_parts([0, 1, 3, 5, 6, 7], step=1)
# [[0, 1], [3], [5, 6, 7]])
Here is a solution utilizing a for loop.
def splitbystep(alist):
newlist = [[alist[0]]]
for i in range(1,len(alist)):
if alist[i] - alist[i-1] == 1:
newlist[-1].append(alist[i])
else:
newlist.append([alist[i]])
return newlist
This is how I'd do it:
inp = [0, 1, 3, 5, 6, 7]
base = []
for item in inp:
if not base or item - base[-1][-1] != 1: # If base is empty (first item) or diff isn't 1
base.append([item]) # Append a new list containing just one item
else:
base[-1].append(item) # Otherwise, add current item to the last stored list in base
print base # => [[0, 1], [3], [5, 6, 7]]
This is the textbook use case for function split_when from module more_itertools:
import more_itertools
print(list(more_itertools.split_when([0, 1, 3, 5, 6, 7], lambda x,y: y-x != 1)))
# [[0, 1], [3], [5, 6, 7]]
Or, even more simple with more_itertools.consecutive_groups:
print([list(g) for g in more_itertools.consecutive_groups([0, 1, 3, 5, 6, 7])])
# [[0, 1], [3], [5, 6, 7]]

Categories

Resources