I am trying to shift the elements of an array cyclically so all elements are replaced with the previous element, and the last rotates to the first poaition, like so: shift(1, [5, 6, 7])=>[7, 5, 6].
The following code only returns [7,5]. Could someone please tell me what is causing this to happen? I went through the code step by step and simply could not find a solution. I also tried 3 different interpreters.
def shift(key, array):
counter = range(len(array)-1)
new = counter
for i in counter:
new[i] = array[i-key]
return new
print shift(1, [5, 6, 7])
range(5) returns [0, 1, 2, 3, 4]. It excludes 5.
Just remove the -1 from range(len(array)-1) and it should work.
You could also use list slicing:
def shift(key, array):
return array[-key:] + array[:-key]
Here is the python way:
def shift(key, array):
return array[-key:]+array[:-key]
You need to remove the -1 from your range:
counter = range(len(array))
If you want a faster method though,
You could instead try using a deque?
from collections import deque
def shift(key, array):
a = deque(array) # turn list into deque
a.rotate(key) # rotate deque by key
return list(a) # turn deque back into a list
print (shift(1, [5, 6, 7]))
The answers are good, but it doesn't work if the key is greater than the length of the array. If you think the key will be larger than the array length, use the following:
def shift(key, array):
return array[key % len(array):] + array[:key % len(array)]
A positive key will shift left and a negative key will shift right.
The numpy package contains the roll function to perform exactly this task:
import numpy as np
b=[5,6,7]
c=np.roll(b,1).tolist()
>>> c
[7, 5, 6]
A function using this and returning a list is:
def shift(array,key):
return np.roll(array,key).tolist()
#!/usr/bin/env python
def ashift(key,array):
newqueue = array[-key:]
newqueue.extend( array[:-key] )
return newqueue
print ashift( 1, [5,6,7] )
print ashift( 2, [5,6,7] )
Results in:
$ ./shift
[7, 5, 6]
[6, 7, 5]
The only potential penalty is if the array is sufficiently large, you may encounter memory issues, as this operation is doing a copy. Using a "key" with an absolute value greater than the length of the array will result in wrapping and results may not be as expected, but will not error out.
Good old fashioned POP & APPEND
arr = [5, 6, 7]
for _ in range(0, 2):
shift = arr.pop(0)
arr.append(shift)
print(arr)
=>[7, 5, 6]
You can use numpy roll
>>> x = np.arange(10)
>>> np.roll(x, 2)
array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
>>> np.roll(x, -2)
array([2, 3, 4, 5, 6, 7, 8, 9, 0, 1])
Related
This question already has answers here:
Sort a part of a list in place
(3 answers)
Closed 3 years ago.
Suppose I have a list [2, 4, 1, 3, 5].
I want to sort the list just from index 1 to the end, which gives me [2, 1, 3, 4, 5]
How can I do it in Python?
(No extra spaces would be appreciated)
TL;DR:
Use sorted with a slicing assignment to keep the original list object without creating a new one:
l = [2, 4, 1, 3, 5]
l[1:] = sorted(l[1:])
print(l)
Output:
[2, 1, 3, 4, 5]
Longer Answer:
After the list is created, we will make a slicing assignment:
l[1:] =
Now you might be wondering what does [1:], it is slicing the list and starts from the second index, so the first index will be dropped. Python's indexing starts from zero, : means get everything after the index before, but if it was [1:3] it will only get values that are in between the indexes 1 and 3, let's say your list is:
l = [1, 2, 3, 4, 5]
If you use:
print(l[1:])
It will result in:
[2, 3, 4, 5]
And if you use:
print(l[1:3])
It will result in:
[2, 3]
About slicing, read more here if you want to.
And after slicing we have an equal sign =, that just simply changes what's before the = sign to what's after the = sign, so in this case, we use l[1:], and that gives [2, 3, 4, 5], it will change that to whatever is after the = sign.
If you use:
l[1:] = [100, 200, 300, 400]
print(l)
It will result in:
[1, 100, 200, 300, 400]
To learn more about it check out this.
After that, we got sorted, which is default builtin function, it simple sorts the list from small to big, let's say we have the below list:
l = [3, 2, 1, 4]
If you use:
print(sorted(l))
It will result in:
[1, 2, 3, 4]
To learn more about it check this.
After that we come back to our first topic about slicing, with l[1:], but from here you know that it isn't only used for assignments, you can apply functions to it and deal with it, like here we use sorted.
Maybe temporarily put something there that's smaller than the rest? Should be faster than the other solutions. And gets as close to your "No extra spaces" wish as you can get when using sort or sorted.
>>> tmp = l[0]
>>> l[0] = float('-inf')
>>> l.sort()
>>> l[0] = tmp
>>> l
[2, 1, 3, 4, 5]
Benchmarks
For the example list, 1,000,000 iterations (and mine of course preparing that special value only once):
sort_u10 0.8149 seconds
sort_chris 0.8569 seconds
sort_heap 0.7550 seconds
sort_heap2 0.5982 seconds # using -1 instead of -inf
For 50,000 lists like [int(x) for x in os.urandom(100)]:
sort_u10 0.4778 seconds
sort_chris 0.4786 seconds
sort_heap 0.8106 seconds
sort_heap2 0.4437 seconds # using -1 instead of -inf
Benchmark code:
import timeit, os
def sort_u10(l):
l[1:] = sorted(l[1:])
def sort_chris(l):
l = l[:1] + sorted(l[1:])
def sort_heap(l, smallest=float('-inf')):
tmp = l[0]
l[0] = smallest
l.sort()
l[0] = tmp
def sort_heap2(l):
tmp = l[0]
l[0] = -1
l.sort()
l[0] = tmp
for _ in range(3):
for sort in sort_u10, sort_chris, sort_heap, sort_heap2, sort_rev:
number, repeat = 1_000_000, 5
data = iter([[2, 4, 1, 3, 5] for _ in range(number * repeat)])
# number, repeat = 50_000, 5
# data = iter([[int(x) for x in os.urandom(100)] for _ in range(number * repeat)])
t = timeit.repeat(lambda: sort(next(data)), number=number, repeat=repeat)
print('%10s %.4f seconds' % (sort.__name__, min(t)))
print()
Use sorted with slicing:
l[:1] + sorted(l[1:])
Output:
[2, 1, 3, 4, 5]
For the special case that you actually have, according to our comments:
Q: I'm curious: Why do you want this? – Heap Overflow
A: I'm trying to make a next_permutation() in python – nwice13
Q: Do you really need to sort for that, though? Not just reverse? – Heap Overflow
A: Yup, reverse is ok, but I just curious to ask about sorting this way. – nwice13
I'd do that like this:
l[1:] = l[:0:-1]
You can define your own function in python using slicing and sorted and this function (your custom function) should take start and end index of the list.
Since list is mutable in python, I have written the function in such a way it doesn't modify the list passed. Feel free to modify the function. You can modify the list passed to this function to save memory if required.
def sortedList(li, start=0, end=None):
if end is None:
end = len(li)
fi = []
fi[:start] = li[:start]
fi[start:end] = sorted(li[start:end])
return fi
li = [2, 1, 4, 3, 0]
print(li)
print(sortedList(li, 1))
Output:
[2, 1, 4, 3, 0]
[2, 0, 1, 3, 4]
I'm working on a probability-related problem. I need to sum only specific items on a certain list.
I've tried using "for" functions and it hasn't worked. I'm looking for a way to select items based on their positions on the list, and summing them.
You can use operator.itemgetter to select only certian index’s in a list or keys in a dict.
from operator import itemgetter
data = [1, 2, 3, 4, 5, 6, 7, 8]
get_indexes = itemgetter(2, 5, 7)
#this will return indexes 2, 5, 7 from a sequence
sum(get_indexes(data)) #3+6+8
#returns 17
That example is for lists but you can use itemgetter for dict keys too just use itemgetter('key2', 'key5', 'key7')({some_dict})
To get only even or odd indexes use slicing not enumerate and a loop it’s much more efficient and easier to read:
even = sum(data[::2])
odd = sum(data[1::2])
You can also use filter but I wouldn’t suggest this for getting by index:
sum(filter(lambda n: data.index(n) % 2 == 0, data))
You really should have put more into your question, but:
stuff = [1, 2, 3, 4, 5, 6, 7, 8]
# sum the numbers that have even indices:
funny_total = sum([x for i, x in enumerate(stuff) if i % 2 == 0 ])
funny_total
# 16
That should get you started. An approach with a for loop would have worked, as well. You just likely have a bug in your code.
stuff = [1, 2, 3, 4, 5, 6, 7, 8]
indices_to_include = [1, 3, 4, 5, 6]
funny_total = 0
for i, x in enumerate(stuff):
if i in indices_to_include:
funny_total += x
You could also:
def keep_every_third(i):
return i % 3 == 0
# variable definitions as above...
for i, x in enumerate(stuff):
if keep_every_third(i):
# do stuff
# left rotate using slicing
def leftRotate(arr, k, n):
arr = arr[k:] + arr[:k]
print(arr)
arr = [1, 2, 3, 4, 5, 6, 7]
leftRotate(arr, 2, 7)
print(arr)
Result:
[3, 4, 5, 6, 7, 1, 2]
[1, 2, 3, 4, 5, 6, 7]
When I print the array outside the function it is not rotated anymore and remains how it originally was. Can someone help me understand this?
Yes, you can change a list from within a function, but you need to use the correct syntax. As you've seen already, this is not the correct way:
def leftRotate(arr, k, n):
arr = arr[k:] + arr[:k]
I will try to explain why this did not work, and hope to give you a better intuition about what really happens. Inside the scope of the function shown above, there are 3 local variables: arr, k, and n. The right-hand side operations arr[k:] + arr[:k] creates a new list object, without modifying the original list, and this resulting object is bound to the local variable name arr. This does not modify the original object, because such assignment statements in Python are never mutating objects. They will only bind a name in a namespace. Think of it as if you're taking the nametag "arr" off of the old list object, which was passed in as argument, and sticking it on the new list object which was just created. The old list object is not modified by such an operation, only the local namespace is modified - the old list object becomes "anonymous" and is no longer reachable in this scope.
The solution is to use a different kind of assignment statement, a slice assignment, which does mutate:
def leftRotate(arr, k, n):
arr[:] = arr[k:] + arr[:k]
As a final note, there is a list-like data structure in stdlib which provides more efficient rotation operations (at the cost of less-efficient indexing into the middle of the collection). If you're interested in this, read the docs on the collections.deque.
The problem is list slicing is not being applied in place. Effectively a new list is created and assigned to a variable arr scoped to leftRotate, i.e. it can can be accessed within your function only. A method which does work in place will work as expected:
def rev_sort(arr, k, n):
arr.sort(reverse=True)
print(arr)
arr = [1, 2, 3, 4, 5, 6, 7]
rev_sort(arr, 2, 7)
print(arr)
[7, 6, 5, 4, 3, 2, 1]
[7, 6, 5, 4, 3, 2, 1]
In your example, you can have your function return a list and assign it to arr:
def leftRotate(arr, k, n):
arr = arr[k:]+arr[:k]
print(arr)
return arr
arr = [1, 2, 3, 4, 5, 6, 7]
arr = leftRotate(arr, 2, 7)
print(arr)
[3, 4, 5, 6, 7, 1, 2]
[3, 4, 5, 6, 7, 1, 2]
Your problem is because you can't change a variable inside a python function because of the scope. Read this for more info.
But resuming, you need to either return arr and assign it outside. Like this:
#left rotate using slicing
def leftRotate(arr, k, n):
arr=arr[k:]+arr[:k]
return arr
arr = [1, 2, 3, 4, 5, 6, 7]
arr = leftRotate(arr, 2, 7)
print arr
Or if you would like, you could make arr a global. (Check this for more info on that). (Don't recommend this last one, but exists)
arr = [1, 2, 3, 4, 5, 6, 7]
#left rotate using slicing
def leftRotate( k, n):
global arr
arr=arr[k:]+arr[:k]
leftRotate( 2, 7)
print arr
Hope it helped :)
There are a lot of really complicated answers. Here's the "for dummies" version:
You are passing arr into leftRotate()
For nearly all purposes, you can think of this as creating another variable, also called arr which leftRotate() works on. leftRotate()'s arr is not the same as the arr you are passing in to leftRotate(). It is a copy (technically it's not a copy until you assign arr to something else, but close enough for these purposes).
You're not getting your modified arr back out of leftRotate() again.
You can solve this in two ways:
Define arr outside of leftRotate(), and don't pass arr in. I'll call this the "global" approach. Not recommended unless you have a very good reason.
Use return arr after your function completes. Essentially, return 'x' means leftRotate() == 'x'. In 99.99999% of cases, this is what you want.
Therefore, in your example, what you really want is this:
#left rotate using slicing
def leftRotate(arr, k, n):
arr=arr[k:]+arr[:k] # not sure this is right, but once you get the return working, it should be easy to debug
# print arr # changed
return arr
arr = [1, 2, 3, 4, 5, 6, 7]
# leftRotate(arr, 2, 7) # changed
arr = leftRotate(arr, 2, 7)
print arr
Given a list of numbers, create a new list of numbers such that the first and last numbers are added and stored as the first number, the second and second-to-last numbers are stored as the second number, and so on
num_list = [1,2,3,4,5,6]
num_list2 = [num_list[-1] + num_list[0], num_list[-2] + num_list[1],
num_list[-3] + num_list[2]]
print(num_list2)
output is [7,7,7]
I got the correct output this way but I am sure this is not an efficient way to do it. Is there a better way? I also am supposed to check for even and odd length of the list and if its an odd number of integers, add the central integer in the original list to the end of the new list but don't know how I would go about doing this
I think this is more efficient, i just simply did a for loop:
num_list2 = []
num_list = [1,2,3,4,5,6]
for i in range(round(len(num_list)/2)):
num_list2.append(num_list[i]+num_list[-(i+1)])
print(num_list2)
Output:
[7, 7, 7]
Let us using reversed
[x + y for x, y in zip(num_list, list(reversed(num_list)))][:len(num_list)//2]
Out[406]: [7, 7, 7]
Here's an inefficient[1], but clear way of doing this:
from itertools import zip_longest # or izip_longest in Python2
lst = [1,2,3,4,5,6]
chop_index = len(lst) // 2 # (or +1, depending on how you want to handle odd sized lists)
lh, rh = lst[:chop_index], lst[:chop_index-1:-1]
print(lh, rh) # To see what's going on in the "chopping"
sums = [x + y for (x,y) in zip_longest(lh, rh, fillvalue=0)]
print(sums)
You could improve it by using islice and reversed iterators, or use index math exclusively.
Output:
lst = [1,2,3,4,5,6] => [7, 7, 7]
lst = [1,2,3,4,5,6,7] => [8, 8, 8, 4]
[1] This makes two copies of the list parts. For long lists this is silly, and you shouldn't use this method. It was mostly written to highlight zip_longest's fillvalue optional argument.
Using itertools.islice on a generator:
from itertools import islice
num_list = [1,2,3,4,5,6]
generator = (x + y for x, y in zip(num_list, num_list[::-1]))
print(list(islice(generator, len(num_list)//2)))
# [7, 7, 7]
You can use the following method, which is compatible with asymmetrical list.
def sum_start_end(list_):
result = [x + y for x, y in zip(list_, list_[::-1])][:len(list_) // 2]
if len(list_) % 2 != 0:
result.append(list_[len(list_) // 2])
return result
so for a symmetric list
>>> num_list = [1, 2, 3, 4, 5, 6]
>>> sum_start_end(num_list)
[7, 7, 7]
and for asymmetric list
>>> num_list = [1, 2, 3, 4, 5, 6, 7]
>>> sum_start_end(num_list)
[8, 8, 8, 4]
It's simpler than you imagine.
Just observe your manual attempt and try to infer from it. We can simply do
x = len(num_list)//2 + len(num_list)%2
for i in range(x):
sumBoth = num_list[i] + num_list[-i-1]
num_list2.append(sumBoth)
or with a simpler one-liner
num_list2 = [ num_list[i] + num_list[-i-1] for i in range(len(num_list)//2+len(num_list)%2)]
This works for even as well as odd lengths because of the len(num_list)%2 at the end in the range.
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.