Python list comprehension mirroring values - python

I tried to figure out the following problem through list comprehension but I couldn't make it work. I will show you how I solved the problem using a loop and a list comprehension.
So, I have a list that can have 0 to 6 elements in a range(6) and when I apply my function on it I want to change the values, as shown here:
l = [0, 1, 2, 3, 4, 5]
mirror = [5, 4, 3, 2, 1, 0]
I don't want to just rotate the array by 180 degrees but I actually want to replace the values. For example, my list looks like this now:
l = [2, 5]
Then l_inverted list should look like this:
l_inverted = [3, 0]
I came up with a regular way to solve it but ever since I started learning Python I've preferred list comprehensions.
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
i = 0
for element in l:
l[i] = mirror[element]
i += 1
This actually inverts the l list. Here's my approach using a list comprehension:
l = [3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
This works fine.
Until:
l = [0, 3, 5]
mirror = [5, 4, 3, 2, 1, 0]
for element in l:
print(element)
l = [mirror[element] if x==element else x for x in l]
So it will replace 5 with 0, 2 with 3 and both 5s (the new one too) become 0. Obviously, I don't want it like that.
Should I stick to the working solution or is there a smooth way to solve it with list comprehensions? I'm trying to practice list comprehensions at all times but it's not fully in my brain yet. Thanks a lot.

If you want it as a list comprehension:
>> l = [0, 3, 5]
>> mirror = [5, 4, 3, 2, 1, 0]
>> l_inverted = [mirror[x] for x in l]
>> l_inverted
[5, 2, 0]

You are drowning in a spoonful of water and trying to take us with you.
You are using bad naming conventions that make your simple problem complicated to comprehend.
orig = [0, 1, 2, 3, 4, 5]
orig_rev = l[::-1]
selector = [0, 3, 5]
result = [orig_rev[i] for i in selector]
print(result ) # [5, 2, 0]

Based on your first two examples, it seems you are looking for the complement of each list value according to some base, similar to ones' complement. You could either hard-code the base as 5 or whatever, or you could assume it's the maximum number in the list and calculate it. Here's a solution for the latter:
Concept
<this complement> = <max value in list> - <this value>
Code
values = [0, 3, 2, 4, 5]
max_value = max(values)
complements = [max_value - value for value in values]
print complements
Result
[5, 2, 3, 1, 0]

Related

How to make weighted list with list comprehension [duplicate]

This question already has an answer here:
flat list as a result of list comprehension [duplicate]
(1 answer)
Closed 12 months ago.
I have a list w where w[i] represents the number of times i should be present in the final result.
For example, if:
w = [1, 3, 4]
then the resulting list should be:
[0, 1, 1, 1, 2, 2, 2, 2]
Notice there is one 0, three 1's, and four 2's.
I have tried to accomplish this with the list comprehension:
w_list = [[i]*w[i] for i in range(len(w))]
but of course this doesn't quite work, giving me this:
[[0], [1, 1, 1], [2, 2, 2, 2]]
How can I write a list comprehension to get me the desired result, like in the example?
You could use a double loop to flatten w_list:
w_list = [x for num, i in enumerate(w) for x in [num] * i]
or
w_list = [x for i in range(len(w)) for x in [i]*w[i]]
Output:
[0, 1, 1, 1, 2, 2, 2, 2]
Here's an alternative solution you might like.
w = [1, 3, 4]
print([i for i in range(len(w)) for _ in range(w[i])])
results in [0, 1, 1, 1, 2, 2, 2, 2] as desired.
Alternatively, [i for i,n in enumerate(w) for _ in range(n)] accomplishes the same thing.

Iterate through a nested list and pick certain elements and create a new list

An example:
list = [[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
So the first index inside a nested list decides which following numbers will be put into an unnested list.
[2, 1, 2, 3, 4] -> 2: so 1 and 2 gets picked up
[0, 4, 5] -> 0: no number gets picked up
[1, 8, 9] -> 1; number 8 gets picked up
Output would be:
[1, 2, 8]
This is what I have so far:
def nested_list(numbers):
if isinstance(numbers[0], list):
if numbers[0][0] > 0:
nested_list(numbers[0][1:numbers[0][0] + 1])
else:
numbers = list(numbers[0])
return numbers + nested_list(numbers[1:])
I try to get the list through recursion but something is wrong. What am I missing or could this be done even without recursion ?
You try using list comprehension with tuple unpacking here.
[val for idx, *rem in lst for val in rem[:idx]]
# [1, 2, 8]
NB This solution assumes you would always have a sub-list of size 1 or greater. We can filter out empty sub-lists using filter(None, lst)
list1=[[2, 1, 2, 3, 4],
[0, 4, 5],
[1, 8, 9]]
list2= []
for nested_list in list1:
for i in range(nested_list[0]):
list2.append(nested_list[i+1])
You can try List-comprehension:
>>> [sub[i] for sub in lst for i in range(1, sub[0]+1) ]
[1, 2, 8]
PS: The solution expects each sublist to be a non-empty list, else it will throw IndexError exception due to sub[0].
Another list comprehension
sum([x[1:x[0] + 1] for x in arr], [])
# [1, 2, 8]
Using builtin function map to apply the picking function, and using itertools.chain to flatten the resulting list of list:
def pick(l):
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( list(map(pick, ll)) )
# [[1, 2], [], [8]]
print( list(itertools.chain.from_iterable((map(pick, ll)))) )
# [1, 2, 8]
Or alternatively, with a list comprehension:
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8]
Two important notes:
I've renamed your list of lists ll rather than list. This is because list is already the name of the builtin class list in python. Shadowing the name of a builtin is very dangerous and can have unexpected consequences. I strongly advise you never to use the name of a builtin, when naming your own variables.
For both solutions above, the error-handling behaves the same: exception IndexError will be raised if one of the sublists is empty (because we need to access the first element to know how many elements to pick, so an error is raised if there is no first element). However, no exception will be raised if there are not enough elements in one of the sublists. For instance, if one of the sublists is [12, 3, 4], then both solutions above will silently pick the two elements 3 and 4, even though they were asked to pick 12 elements and not just 2. If you want an exception to be raised for this situation, you can modify function pick in the first solution:
def pick(l):
if len(l) == 0 or len(l) <= l[0]:
raise ValueError('in function pick: two few elements in sublist {}'.format(l))
return l[1:1+l[0]]
ll = [[2, 1, 2, 3, 4], [0, 4, 5], [1, 8, 9], [12, 3, 4]]
print( [x for l in ll for x in l[1:1+l[0]]] )
# [1, 2, 8, 3, 4]
print( [x for l in ll for x in pick(l)] )
# ValueError: in function pick: two few elements in sublist [12, 3, 4]

How to split a numpy array based on a tuple content? [duplicate]

This question already has an answer here:
Create index list for np.split from the list that already has number for each section
(1 answer)
Closed 3 years ago.
Let's say I've got an array [0, 1, 2, 3, 4, 5, 6, 7] and a tuple: (3, 3, 2).
I'm looking for a way to split my array to 3 array based on my tuple data:
[0, 1, 2]
[3, 4, 5]
[6, 7]
I can write a simple code like this to get what I want, however I'm looking for a correct and pythonic way to do this:
I used lists for simplicity.
a = [0, 1, 2, 3, 4, 5, 6, 7]
b = (3, 3, 2)
pointer = 0
for i in b:
lst = []
for j in range(i):
lst.append(a[pointer])
pointer += 1
print(lst)
Or this one:
a = [0, 1, 2, 3, 4, 5, 6, 7]
b = (3, 3, 2)
pointer = 0
for i in b:
lst = a[pointer:pointer+i]
pointer += i
print(lst)
Results:
[0, 1, 2]
[3, 4, 5]
[6, 7]
you can use the split method of numpy
import numpy as np
a = [0, 1, 2, 3, 4, 5, 6, 7]
b = (3, 3, 2)
c = np.split(a, np.cumsum(b)[:-1])
for r in c:
print(r)
np.split(a, b) splits a by the indices in b along a given axis(0 by default).
If you don't want to modify your input list, you can use an iterator and the itertools module.
>>> from itertools import islice
>>> a = [0, 1, 2, 3, 4, 5, 6, 7]
>>> b = (3, 3, 2)
>>> i = iter(a)
>>> [list(islice(i, x)) for x in b]
[[0, 1, 2], [3, 4, 5], [6, 7]]
In the first step you create an iterator, which starts at the first element of a. Then you iterate in a list comprehension over your numbers in b and in each step you pull accordingly many elements from the iterator and store them in your result list.
One simpler way is this:
a = [0, 1, 2, 3, 4, 5, 6, 7]
b = (3, 3, 2)
for ind in b:
print(a[:ind])
a = a[ind:]
It loops through slice sizes in b while shortening the original array every time. You can easily append the resulting slices as sublists if you need them for something else. It's almost like one of your solutions except it doesn't use any extra variables and iterates directly through elements of b.
Also, I wouldn't call variables a and b - surely not in this case where variables have clear meanings that you can express through their names. More meaningful names lessen bugs number and make code more clear, becomes a real difference with larger/more complex code. I'd call a at least in_list and b slices, but with more context this could be better.
The most "concise" syntax would be :
ex_array = [0, 1, 2, 3, 4, 5, 6, 7]
extuple = (3, 3, 2)
result = [ex_array[sum(extuple[:iii]):sum(extuple[:iii])+extuple[iii]] for iii in range(len(extuple))]
result would be a list of the expected sub-lists
Re-using the pairwise function from Compare two adjacent elements in same list, you could also:
from itertools import accumulate
from more_itertools import pairwise
a = [0, 1, 2, 3, 4, 5, 6, 7]
b = (3, 3, 2)
[a[slice(*s)] for s in pairwise(accumulate((0,)+b))]
That begin said, the np.split answer is probably faster (and easier to read).

Creating a new list when before it reaches a number

How do I create a new list that contains sublists of ints but the way of divide it is when the next number is the minimun (or equal to the first value founded)?
For example
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
The output that I am looking for is shown below:
Complete_List=[[1,2,3,4,5],[1,2,3,4],[1,2,3,4,5,6]]
I tried looping through the list and appending it when the value is greater than 1 . However it will not work as it doesn't create another list inside it.
Do I have to right a regex for this problem?
Some guidance would be really helpful.
Thank you
Here's something that will split a generic iterable on a given value.
def split_on_value(iterable, split_value):
iterator = iter(iterable)
outer, inner = [], [next(iterator)]
for value in iterator:
if value == split_value:
outer.append(inner)
inner = []
inner.append(value)
outer.append(inner)
return outer
value_list = [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
print split_on_value(value_list, 1)
# [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
print split_on_value(value_list, 3)
# [[1, 2], [3, 4, 5, 1, 2], [3, 4, 1, 2], [3, 4, 5, 6]]
A vanilla, straightforward, CS101 solution. Though, possibly the most efficient one, because it scans the list exactly once. It also does not assume that segments begin with 1.
fragment = []
result = []
prev = List1[0] - 1 # Preset the previous element marker
for n in List1:
if n > prev:
fragment.append(n)
else:
result.append(fragment)
fragment = [n]
prev = n
result.append(fragment)
#[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
First you search for the 1's, or whatever your condition is, and get the indices within the list. Don't forget to append the len(list) to include the last segment.
idx = [i for i, l in enumerate(List1) if l == 1] + [len(List1)]
Optional, if you want the beginning end of the List. That is, you do not know if there will be a 1 always at index 0.
idx = [0] + idx if idx[0] != 0 else idx
Then, split the list at those indices you found.
complete_list = [List1[ind1:ind2] for ind1, ind2 in zip(idx[:-1], idx[1:])]
and the result:
[[1, 2, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
You can try this to split at every instance of 1:
List1=[1,2,3,4,5,1,2,3,4,1,2,3,4,5,6]
print [map(int, list("1"+i)) for i in ''.join(map(str, List1)).split("1")][1:]
By mapping over List1 with the string function, we can then join all the numbers in the list into one large string. From there, the algorithm splits itself at each instance of one, creating a list containing the new strings of digits. from there, the code maps the integer function over a list created of the strings and appending 1 at the front of the string to make up for the lost 1 when it originally split, creating a list within a list.

Is it possible to insert specific sections of one list into a specific location in another list?

I'm fairly new to Python, and besides finding it useful and rather easy to understand for the most part, there are still some things I'm unclear of, ergo this question.
Is it possible to insert specific sections of one list into a specific location in another list?
Say for example, I have a list called 'a', and in this list I have the numbers 1, 3 and 5 in this format:
a = [1, 3, 5]
I also have a list called 'b' which contains the numbers 2 and 4 in this format:
b = [2, 4]
My end goal would be for list 'a' to be changed to this:
a = [1, 2, 3, 4, 5]
As you can see, this would require me to specify using indices for both lists to combine them into one list in this custom format, and I am unsure as to how I could go about this.
I unintentionally left out a major detail, and that is the fact that I wanted to make use of the 'insert' function rather than 'append'.
Yes. Just use insert() and have the second argument be a list element.
a = [1, 3, 5]
b = [2, 4]
# The first argument is the index to insert it at.
# The second is the actual item to be inserted.
a.insert(1, b[0]) # [1, 2, 3, 5]
a.insert(3, b[1]) # [1, 2, 3, 4, 5]
Note that if you just want a sorted, you can just use this:
for item in b:
a.append(item) # Order does not matter, we will sort it later
# a is currently [1, 3, 5, 2, 4] since append() adds to the end of the list
a.sort()
# a is now [1, 2, 3, 4, 5] as sort() re-ordered it
Or if you want it even simpler, concatenate them and then sort:
a = a + b # [1, 3, 5, 2, 4]
a.sort() # [1, 2, 3, 4, 5]
Let me know if this is unclear or not what you wanted.
How does this work for you?
a = [1,2,5,6,7]
b = [3,4]
for n,k in enumerate(a):
if(k<b[0]):
a = a[0:n]+b+a[n:]
break
print(a)
using the colon to slice arrays is super useful.
If you're interleaving the lists, you can zip them using izip_longest:
>>> a = [1, 3, 5]
>>> b = [2, 4]
>>> from itertools import izip_longest
>>> [c for pair in izip_longest(a, b)
... for c in pair if c is not None]
[1, 2, 3, 4, 5]

Categories

Resources