Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
i'm trying to compress a list using generator:
examples
[1, 1, 1, 1, 2, 2, 2, 1, 1, 1] == [1, 2, 1]
[5, 5, 5, 4, 5, 6, 6, 5, 5, 7, 8, 0, 0])) == [5, 4, 5, 6, 5, 7, 8, 0]
I tried to use a generator that checks if the 1st and 2nd element are equal then check 2nd and 3rd and so on until it is no longer equal "when it reaches 4" and then yield "5" then it would repeat the process starting with "4"
code
test = [5, 5, 5, 4, 5, 6, 6, 5, 5, 7, 8, 0, 0] # sample list
from typing import Iterable
def compress(items: list) -> Iterable:
x = 0
while items[x] == items[x + 1]:
x += 1
yield items[x]
ans = compress(test)
for x in ans:
print(ans)
but i keep getting
generator object compress at 0x00000254D383C820.
why won't it loop?
if i try and use next() it only goes up to 5 and wont check the other numbers.
any assistance is greatly appreciated.
As others have explained, your structure is incorrect - you're only encountering the yield once outside the loop. The ideal way would be to iterate over pairs of consecutive numbers and yield the first one in the loop if they are different.
However, here's a canonical method via itertools.groupby which removes consecutive duplicates:
from itertools import groupby
from operator import itemgetter
list(map(itemgetter(0), groupby(l)))
# [1, 2, 1]
So there are several flaws, all of them described as comments to the question post.
there is a loop missing that would yield more than one value
you print ans and not x, which logically is the generator object.
Is this code working for you?
test = [5, 5, 5, 4, 5, 6, 6, 5, 5, 7, 8, 0, 0]
def compress(items):
for i, d in enumerate(items[:-1]):
if d == items[i+1]:
continue
yield d
yield items[-1]
for x in compress(test):
print(x)
If you're looking for the same effect as that, look into the itertools.groupby function. It's smarter to use built-in tools.
If you're interested in learning generators, write it. You didn't make any mistakes outside of having "yield" at the wrong level, but you wrote un-Pythonic code. Keeping track of x and x+1 and keeping the whole thing in memory are all bad.
class NeverInList: pass
from typing import Iterable
def compress(items: list) -> Iterable:
previous = NeverInList() # Something never in a list, duh.
for item in items:
if item != previous:
yield item
previous = item
ans = compress(test)
for x in ans:
print(x) # "x", not "ans"
Heh, I recognize this question from somewhere. ;)
I went about it a different way than you but I figured you might find it useful to peep at:
def compress(i:list):
return[i[x]for x in range(len(i))if x==0 or i[x-1]!=i[x]]
The other answers fit the specifics of your question better, I just happened to recognize the setup and figured I'd dig up and offer the solution I gave to this very same problem. Cheers!
One more solution: go through the combination of the list and the same list shifted by one (in other words, by the pairs of consecutive items) and select the second item from each pair where the first (previous) item differs from the second (next) item:
l = [5, 5, 5, 4, 5, 6, 6, 5, 5, 7, 8, 0, 0]
[l[0]] + [y for x,y in zip(l, l[1:]) if x!=y]
#[5, 4, 5, 6, 5, 7, 8, 0]
Incidentally, this is one of the fastest solutions presented so far.
you need to implement stack, and see if last element of list is same as value or not
test = [5, 5, 5, 4, 5, 6, 6, 5, 5, 7, 8, 0, 0]
def funct(lis):
l = []
for val in lis:
if len(l) ==0:
l.append(val)
yield val
if l[-1] != val:
l.append(val)
yield val
for result in funct(test):
print(result)
output
5
4
5
6
5
7
8
0
way two, using comparison variable in O(N) time complexity and O(1) space complexity
def funct(lis):
checker = None
for val in lis:
if checker != val:
checker = val
yield val
I would like to append to a new list all elements of an existing list of lists after a specific point
m = [[1,2,3],[4,5,10],[6,2,1]]
specific point = m[0][2]
newlist = [3,4,5,10,6,2,1]
You can directly slice off the remainder of the first target list and then add on all subsequent elements, eg:
m = [[1,2,3],[4,5,10],[6,2,1]]
y, x = 0, 2
new_list = m[y][x:] + [v for el in m[y+1:] for v in el]
# [3, 4, 5, 10, 6, 2, 1]
Here's a couple of functional approaches for efficiently iterating over your data.
If sublists are evenly sized, and you know the index from where to begin extracting elements, use chain + islice:
from itertools import chain, islice
n = 3 # Sublist size.
i,j = 0,2
newlist = list(islice(chain.from_iterable(m), i*n + j, None))
If you don't know the size of your sublists in advance, you can use next to discard the first portion of your data.
V = chain.from_iterable(m)
next(v for v in V if v == m[i][j])
newlist = list(V)
newlist.insert(m[i][j], 0)
This assumes there is no identical value earlier in the sequence.
You can put a conditional in your iteration and only add based on that condition. Once you hit that specific index, make your condition true. Something like this:
m = [[1,2,3],[4,5,10],[6,2,1]]
specific_point = (0,2)
newlist = [3,4,5,10,6,2,1]
output = []
for i in range(len(m)):
for j in range(len(m[i])):
if (i,j) < specific_point:
continue
output.append(m[i][j])
output:
[3, 4, 5, 10, 6, 2, 1]
why not flatten the initial list and go from there
flat_list = [item for sublist in m for item in sublist]
would return [1,2,3,4,5,10,6,2,1] so now you're really on flat_list[2:]
Most of the answers only work for this specific shape of nested list, but it's also possible to create a solution that works with any shape of nested list.
def flatten_from(sequence, path=[]):
start = path.pop(0) if path else 0
for item in sequence[start:]:
if isinstance(item, (list, tuple)):
yield from flatten_from(item, path)
else:
yield item
With the example from the question
>>> list(flatten_from([[1, 2, 3], [4, 5, 10], [6, 2, 1]], [0, 2]))
[3, 4, 5, 10, 6, 2, 1]
It also works with any shape and level of nesting of the input data
m = [[1], [[2], [3, 4, 5, 6, 7]], 8, [9, [10, 11]]]
flatten_from(m, [])) # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
flatten_from(m, [2]) # 8, 9, 10, 11
flatten_from(m, [1, 1, 3]) # 6, 7, 8, 9, 10, 11
This is a bit of a bastard algorithm, though. On one hand, it uses nice functional programming concepts: recursion and yield.
On the other hand it relies on the side effect of mutating the path argument with list.pop, so it's not a pure function.
Below solution will work for your case where your array is restricted to list of list and the size of 'sublist' is consistent throughout i.e "3" in your case
m = [[1,2,3],[4,5,10],[6,2,1]] #input 2D array
a, b = 0, 2 #user input --> specific point a and b
flat_list_m = [item for firstlist in m for item in firstlist] #flat the 2D list
print (flat_list_m[len(m[0])*a+b:]) #print from specific position a and b, considering your sublist length is consistent throughout.
I hope this helps! :)
I am trying to reorder items in a list in a way illustrated by the following example:
Suppose the list before reordering is:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
I want to implement a method called reorder_list(list, custom_order) such that:
list1 = reorder_list(list1, [3, 6, 12, 9])
print(list1)
Out: [3, 6, 9, 1, 2, 4, 5, 7, 8, 10]
Explanation: [3, 6, 12, 9] is a custom order I am specifying. 12 is not in list1 so it will be ignored. 3,6,9 are in list1, so they get moved to the front of the list and their order is the same as in [3, 6, 12, 9]. The remaining items in list1 are after 3,6,9 and in the original order.
Is there is an easier way (and a Pythonic way) than implementing the C-like loop code. For my purpose I care more about code simplicity than performance.
def reorder_list(items, early):
moved = [item for item in early if item in items]
remain = [item for item in items if item not in moved]
return moved + remain
This is really the same algorithm as Gireesh and Stephen Rauch wrote. Gireesh's version is written as it would be before list comprehensions, while Stephen's uses sets for faster lookups (but converts both input lists to sets; one should suffice) and extends with a generator expression instead of allocating a second list.
One thing of note is that we've assumed items are unique within the lists. Both in and set expect this.
00sdf0's answer uses a very different algorithm that might make sense in Haskell, with its lazy evaluation and tail call optimization, but in this case seems neither easily understood nor performant. It can be more clearly rewritten using slices:
def reorder_list(items, early):
result = list(items)
for move in reversed(early):
try:
place = result.index(move)
result = [result[place]] + result[:place] + result[place+1:]
except ValueError:
pass # this item wasn't in the list
This does allocate more lists, effectively shallow copying the list twice per moved item. Using islice instead of slice produced lazy evaluation that avoided one of those copies.
def reorder_list(list_main, custom_order):
# initializing empty list
list1 = list()
# to add values of custom list to list1 which are present in main list
for value in custom_order:
# add only the values which are present in main list
if value in list_main:
list1.append(value)
# to add remaining element of main list to list1 which are not present in list1
for value in list_main:
if value not in list1:
list1.append(value)
return list1
list1 = [1,2,3,4,5,6,7,8,9,10]
list1 = reorder_list(list1, [3,6,12,9])
print(list1)
A couple of list comprehensions should be reasonably performant for this:
Code:
def reorder_list(list_to_reorder, custom_order):
new_list = [x for x in custom_order if x in set(list_to_reorder)]
new_list.extend(x for x in list_to_reorder if x not in set(custom_order))
return new_list
Test Code:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(reorder_list(list1, [9, 6, 3, 12]))
Results:
[9, 6, 3, 1, 2, 4, 5, 7, 8, 10]
The problem may be solved in the following way using itertools.chain and itertools.islice.
from itertools import chain, islice
lst = [1,2,3,4,5,6,7,8,9,10]
items_to_move = [9,6,3,12]
# move index i to front of list
def front(seq, i):
item = islice(seq, i, i+1)
start = islice(seq, 0, i, None)
end = islice(seq, i+1, None)
return list(chain(item,start,end))
for item in reversed(items_to_move):
if item in lst:
lst = front(lst, lst.index(item))
Output:
[9, 6, 3, 1, 2, 4, 5, 7, 8, 10]
I am having problems with this and can't figure out why my code is not working. Could someone help me out with this, please? I made this:
def f8(a_list, n):
"""
The parameter a_list is a list of int's. The parameter n is an int.
The function f8() should return a list that contains exactly one
of each number in a_list that occurs less than n times in a_list.
Example:
f8([1, 1, 7, 7, 7, 3, 3, 3, 4, 4, 4, 5, 5], 3) should return
[1, 5] (as 1 and 5 are the only numbers that occur less than 3
times in a_list)
"""
k = []
for i in a_list:
if i < a_list.count(n):
k.append(i)
return k
print f8([1, 7, 7, 3, 3, 3, 4, 4, 5], 2)
I would expect it should print [1,5], but it just gives me a None. Why is that? Could someone help me out, please? I am stuck here.
You have the counting the wrong way around! You need to count the occurrences of i and compare this against n. Also, you need to move the return outside the for-loop. Finally, to remove the repeats in the final list, we should iterate through set(a_list) so that we only iterate through unique elements. The neat thing about this is that since we are counting the occurrences in the original a_list, we don't need to create any copies or anything fiddly to deal with this.
This makes your function:
def f8(a_list, n):
k = []
for i in set(a_list):
if a_list.count(i) < n:
k.append(i)
return k
which works if we give it a test:
>>> f8([1, 1, 7, 7, 7, 3, 3, 3, 4, 4, 4, 5, 5], 3)
[1, 5]
>>> f8([1, 1, 2, 2, 2, 2, 4, 5, 6, 7, 7, 7, 7], 3)
[1, 4, 5, 6]
Note, if you wanted to shorten the function, you could achieve the same result in a one line list-comprehension:
def f8(a_list, n):
return [i for i in set(a_list) if a_list.count(i) < n]
which gives the same outputs to the tests as above.
Firstly, you should not call count repeatedly. Each call iterates the entire list. You can get all counts in one go using collections.Counter. Secondly, you need to check if you have added any element before to avoid duplicates, just calling set one a_list will not guarantee order of appearance:
from collections import Counter
def f8(a_list, n):
c = Counter(a_list)
k, seen = [], set()
for x in a_list:
if c[x] < n and x not in seen:
seen.add(x)
k.append(x)
return k
The condition to append in your code is never met, thus, no value is being returned. Instead, return after the loop is finished. Also, your counting condition is reversed, as you should be finding the count of i occurring in the list, not n:
k = []
for i in a_list:
if a_list.count(i) < n and i not in k:
k.append(i)
return k
The reason why you are receiving None is that the return statement does not pass, so to satisfy the variable storage during the function call, None is returned.
I'm looking for maximum absolute value out of chunked list.
For example, the list is:
[1, 2, 4, 5, 4, 5, 6, 7, 2, 6, -9, 6, 4, 2, 7, 8]
I want to find the maximum with lookahead = 4. For this case, it will return me:
[5, 7, 9, 8]
How can I do simply in Python?
for d in data[::4]:
if count < LIMIT:
count = count + 1
if abs(d) > maximum_item:
maximum_item = abs(d)
else:
max_array.append(maximum_item)
if maximum_item > highest_line:
highest_line = maximum_item
maximum_item = 0
count = 1
I know I can use for loop to check this. But I'm sure there is an easier way in python.
Using standard Python:
[max(abs(x) for x in arr[i:i+4]) for i in range(0, len(arr), 4)]
This works also if the array cannot be evenly divided.
Map the list to abs(), then chunk the list and send it to max():
array = [1,2,4,5,4,5,6,7,2,6,-9,6,4,2,7,8]
array = [abs(item) for item in array]
# use linked question's answer to chunk
# array = [[1,2,4,5], [4,5,6,7], [2,6,9,6], [4,2,7,8]] # chunked abs()'ed list
values = [max(item) for item in array]
Result:
>>> values
[5, 7, 9, 8]
Another way, is to use islice method from itertools module:
>>> from itertools import islice
>>> [max(islice(map(abs,array),i,i+4)) for i in range(0,len(array),4)]
[5, 7, 9, 8]
To break it down:
1 - map(abs, array) returns a list of all absolute values of array elemets
2 - islice(map(abs,array),i,i+4)) slices the array in chunks of four elements
3 - i in range(0,len(array),4) stepping range for islice to avoid overlapping
This can be wrapped in function as fellows:
def max_of_chunks(lst, chunk_size):
lst = map(abs, lst)
result = [max(islice(lst,i,i+chunk_size)) for i in range(0,len(lst),chunk_size)]
return result
Upd: Oh, I've just seen newest comments to task and answers. I wasn't get task properly, my bad :) Let my old answer stay here for history. Max numbers from list chunks you can find in the way like that:
largest = [max(abs(x) for x in l[i:i+n]) for i in xrange(0, len(l), n)]
or
largest = [max(abs(x) for x in l[i:i+n]) for i in range(0, len(l), n)]
if you're use Python3.
Original answer just for history: If you had to choice some numbers (once) from not a big list, you shouldn't install big libraries like numpy for such simple tasks. There are a lot of techniques to do it with built-in Python tools. Here they are (something of them).
So we have some list and count of maximum different elements:
In [1]: l = [1, 2, 4, 5, 4, 5, 6, 7, 2, 6, -9, 6, 4, 2, 7, 8]
In [2]: n = 4
A. First we getting only unique numbers from source list by converting it to set. Then we creating a list consist of these unique numbers, sort it and finally get N last (greatest) elements:
In [3]: sorted(list(set(l)))[-n:]
Out[3]: [5, 6, 7, 8]
B. You can use built-in heapq module:
In [7]: import heapq
In [8]: heapq.nlargest(n, set(l))
Out[8]: [8, 7, 6, 5]
Of course you can 'wrap' A or B technique into some human-friendly function like def get_largest(seq, n): return sorted(list(set(l)))[-n:]. Yes I've ommited some details like handling IndexError. You should remember about it when you'll writing the code.
C. If your list(s) is very long and you had to do many of these operations so fast as Python can, you should use special third-party libraries like numpy or bottleneck.