Generating circular shifts / reduced Latin Squares in Python - python

Was just wondering what's the most efficient way of generating all the circular shifts of a list in Python. In either direction. For example, given a list [1, 2, 3, 4], I want to generate either:
[[1, 2, 3, 4],
[4, 1, 2, 3],
[3, 4, 1, 2],
[2, 3, 4, 1]]
where the next permutation is generated by moving the last element to the front, or:
[[1, 2, 3, 4],
[2, 3, 4, 1],
[3, 4, 1, 2],
[4, 1, 2, 3]]
where the next permutation is generated by moving the first element to the back.
The second case is slightly more interesting to me because it results in a reduced Latin square (the first case also gives a Latin square, just not reduced), which is what I'm trying to use to do experimental block design. It actually isn't that different from the first case since they're just re-orderings of each other, but order does still matter.
The current implementation I have for the first case is:
def gen_latin_square(mylist):
tmplist = mylist[:]
latin_square = []
for i in range(len(mylist)):
latin_square.append(tmplist[:])
tmplist = [tmplist.pop()] + tmplist
return latin_square
For the second case its:
def gen_latin_square(mylist):
tmplist = mylist[:]
latin_square = []
for i in range(len(mylist)):
latin_square.append(tmplist[:])
tmplist = tmplist[1:] + [tmplist[0]]
return latin_square
The first case seems like it should be reasonably efficient to me, since it uses pop(), but you can't do that in the second case, so I'd like to hear ideas about how to do this more efficiently. Maybe there's something in itertools that will help? Or maybe a double-ended queue for the second case?

You can use collections.deque:
from collections import deque
g = deque([1, 2, 3, 4])
for i in range(len(g)):
print list(g) #or do anything with permutation
g.rotate(1) #for right rotation
#or g.rotate(-1) for left rotation
It prints:
[1, 2, 3, 4]
[4, 1, 2, 3]
[3, 4, 1, 2]
[2, 3, 4, 1]
To change it for left rotation just replace g.rotate(1) with g.rotate(-1).

For the first part, the most concise way probably is
a = [1, 2, 3, 4]
n = len(a)
[[a[i - j] for i in range(n)] for j in range(n)]
# [[1, 2, 3, 4], [4, 1, 2, 3], [3, 4, 1, 2], [2, 3, 4, 1]]
and for the second part
[[a[i - j] for i in range(n)] for j in range(n, 0, -1)]
# [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
These should also be much more efficient than your code, though I did not do any timings.

variation on slicing "conservation law" a = a[:i] + a[i:]
ns = list(range(5))
ns
Out[34]: [0, 1, 2, 3, 4]
[ns[i:] + ns[:i] for i in range(len(ns))]
Out[36]:
[[0, 1, 2, 3, 4],
[1, 2, 3, 4, 0],
[2, 3, 4, 0, 1],
[3, 4, 0, 1, 2],
[4, 0, 1, 2, 3]]
[ns[-i:] + ns[:-i] for i in range(len(ns))]
Out[38]:
[[0, 1, 2, 3, 4],
[4, 0, 1, 2, 3],
[3, 4, 0, 1, 2],
[2, 3, 4, 0, 1],
[1, 2, 3, 4, 0]]

more_itertools is a third-party library that offers a tool for cyclic permutations:
import more_itertools as mit
mit.circular_shifts(range(1, 5))
# [(1, 2, 3, 4), (2, 3, 4, 1), (3, 4, 1, 2), (4, 1, 2, 3)]
See also Wikipedia:
A circular shift is a special kind of cyclic permutation, which in turn is a special kind of permutation.

The answer by #Bruno Lenzi does not seem to work:
In [10]: from itertools import cycle
In [11]: x = cycle('ABCD')
In [12]: print [[x.next() for _ in range(4)] for _ in range(4)]
[['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]
I give a correct version below, however the solution by #f5r5e5d is faster.
In [45]: def use_cycle(a):
x=cycle(a)
for _ in a:
x.next()
print [x.next() for _ in a]
....:
In [46]: use_cycle([1,2,3,4])
[2, 3, 4, 1]
[3, 4, 1, 2]
[4, 1, 2, 3]
[1, 2, 3, 4]
In [50]: def use_slice(a):
print [ a[n:] + a[:n] for n in range(len(a)) ]
....:
In [51]: use_slice([1,2,3,4])
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
In [54]: timeit.timeit('use_cycle([1,2,3,4])','from __main__ import use_cycle',number=100000)
Out[54]: 0.4884989261627197
In [55]: timeit.timeit('use_slice([1,2,3,4])','from __main__ import use_slice',number=100000)
Out[55]: 0.3103291988372803
In [58]: timeit.timeit('use_cycle([1,2,3,4]*100)','from __main__ import use_cycle',number=100)
Out[58]: 2.4427831172943115
In [59]: timeit.timeit('use_slice([1,2,3,4]*100)','from __main__ import use_slice',number=100)
Out[59]: 0.12029695510864258
I removed the print statement in use_cycle and use_slice for timing purposes.

Using itertools to avoid indexing:
x = itertools.cycle(a)
[[x.next() for i in a] for j in a]

This will be my solution.
#given list
a = [1,2,3,4]
#looping through list
for i in xrange(len(a)):
#inserting last element at the starting
a.insert(0,a[len(a)-1])
#removing the last element
a = a[:len(a)-1]
#printing if you want to
print a
This will output the following:
[4, 1, 2, 3]
[3, 4, 1, 2]
[2, 3, 4, 1]
[1, 2, 3, 4]
You can also use pop instead of using list slicing but the problem with pop is that it will return something.
Also the above code will work for any length of list. I have not checked for performance of the code. I am assuming that it will work better.
You should have a look at Python docs for getting a good understanding of List slicing.

Related

Trying to write a combinations function in Python on my own, not working

I'm trying to solve this problem here: https://codingbat.com/prob/p252079?parent=/home/peter#norvig.com
In math, a "combination" of a set of things is a subset of the things. We define the function combinations(things, k) to be a list of all the subsets of exactly k elements of things. Conceptually, that's all there is, but there are some questions to settle: (A) how do we represent a subset? (B) What order are the elements within each subset? (C) What order to we list the subsets? Here's what we will agree to: (A) a subset will be a list. (B) The order of elements within a list will be the same as the order within 'things'. So, for example, for combinations([1, 2, 3], 2) one of the subsets will be [1, 2]; whereas [2, 1] is not a subset. (C) The order of subsets will be lexicographical or sorted order -- that is, combinations([1, 2, 3], 2) returns [ [1, 2], [1, 3], 2, 3] ] because [1, 2] < [1, 3] < [2, 3]. You might want to use the function 'sorted' to make sure the results you return are properly ordered.
combinations([1, 2, 3, 4, 5], 2) → [[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
combinations([1, 2, 3], 2) → [[1, 2], [1, 3], [2, 3]]
combinations([1, 2, 3, 4, 5, 6], 5) → [[1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 5, 6], [1, 2, 4, 5, 6], [1, 3, 4, 5, 6], [2, 3, 4, 5, 6]]
Here's my code:
def combinations(things, k):
if k == 0 or k == len(things):
return [things]
elif len(things) < k:
return
else:
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for i in range(len(combinations(things[1:], k - 1))):
firstelement = [things[0]]
firstelement += combinations(things[1:], k - 1)[i]
finalcomb.append(firstelement)
for j in range(len(combinations(things[1:], k))):
finalcomb.append(combinations(things[1:], k)[j])
return finalcomb
However, this is the output:
Haven't hit 10 reputation yet so it's a link to the error. I'm not sure what I did wrong, can anybody help me out? Thank you so much.
The problem is this. When k == 0 it shouldn't return [things]. It should return an empty array. Similar to when len(things) < k:. This is because, when k == 0, it means we that we have already found all the numbers for that specific combination.
But there's one more problem. We're returning an empty array. However, in the for loops, we're iterating over the returned array. So if the array is empty, nothing happens. So what we should really return is an empty 2D array. I won't go into too much detail about what the problem is since it's better for you to try and understand why it's not working. Try adding print statements inside and outside the for loops.
Anyway, the working code looks like this:
def combinations(things, k):
if k == len(things):
return [things[:]]
if len(things) < k or k == 0:
return [[]]
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for comb in subcomb1:
firstelement = [things[0]]
firstelement += comb
finalcomb.append(firstelement)
finalcomb += subcomb2
return finalcomb
Note a few things:
Use the variables you've already assigned (I'm assuming you forgot about them)
Lists can be concatenated using +, similar to strings. If you return within an if statement, you don't need an else for the next line since if the if statement is satisfied, it would definitely not go to the else.
You simply can try using itertools:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 2):
output.append(list(nums))
print(output)
output:
[[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
For 3 nums:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 3):
output.append(list(nums))
print(output)
Output:
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

Slicing a list to extract the last k and next k elements given an index

I'm trying to write a smart snippet of code that does the following:
Given a list and an integer-valued parameter k:
k = 2
myList = [1, 2, 3, 4, 5]
I would like to find a way of slicing my list such that I can later construct the following dictionary:
{1: [5, 4, 2, 3],
2: [1, 5, 3, 4],
3: [2, 1, 4, 5],
4: [3, 2, 5, 1],
5: [4, 3, 1, 2]}
i.e, I need to slice my list and extract my last k and next k elements (the order of the elements in the list after slicing does not matter), given an index.
For example, if my index is 0, then I would expect [5, 4, 2, 3].
My problem is similar to this question. However not exactly the same.
I'd appreciate any help, hint, or reference to any source.
You could do:
k = 2
myList = [1, 2, 3, 4, 5]
offset = len(myList)
padded_list = myList * 3
result = {myList[i]: padded_list[j - k: j][::-1] + padded_list[j + 1: j + k + 1] for i, j in
zip(range(offset), range(offset, 2 * offset))}
print(result)
Output
{1: [5, 4, 2, 3], 2: [1, 5, 3, 4], 3: [2, 1, 4, 5], 4: [3, 2, 5, 1], 5: [4, 3, 1, 2]}
The idea is to pad the list with itself before and after, and the iterate in the middle section. This should work without problem while k < len(myList).
I think simple python list slicing should suffice. Basically I sliced the list two times and concatenated the resulting two lists.
>>> l = [1, 2, 3, 4, 5]
>>> k = 2
>>> l[:k-1] + l[k:]
[1, 3, 4, 5]
Good luck!

Python program to shift elements of array

I am trying to create a python program to shuffle an array so that the horizontal and vertical rows never have a repeat number.
Input: [1,2,3,4]
Output:
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
My program calculates the shifting of each element correctly, but when it appends the list to the output list, the output list only has repeat copies of the last item in the list.
def numbers(list_of_numbers):
finalValues = [list_of_numbers]
#print(list_of_numbers)
for i in range(1,len(list_of_numbers)):
print("Last array of final: ", finalValues[-1])
tempArray = finalValues[-1]
print("Temp Array: ",tempArray)
temp = tempArray[0]
for j in range(0,len(list_of_numbers)-1):
tempArray[j] = tempArray[j+1]
tempArray[-1] = temp
finalValues.append(tempArray)
print("Final Values: ",finalValues)
return finalValues
numbers([1,2,3,4])
Program Output
[[4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3]]
Correct Output
[[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]]
The problem comes from the line:
tempArray = finalValues[-1]
You don't create a copy of the previous list, but only a new name to refer to it. After that, all changes you make to tempArray are actually changes to this list, and when you finally do:
finalValues.append(tempArray)
you just add another reference to this same list in finalValues.
In the end, finalValues contains 4 references to the same list, which you can access with finalValues[0], finalValues[1]...
What you need is to create a new list by copying the previous one. One way to do it is to use a slice:
tempArray = finalValues[-1][:]
You can find other ways to close or copy a list in this question
And so, the complete code gives the expected output:
Last array of final: [1, 2, 3, 4]
Temp Array: [1, 2, 3, 4]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1]]
Last array of final: [2, 3, 4, 1]
Temp Array: [2, 3, 4, 1]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2]]
Last array of final: [3, 4, 1, 2]
Temp Array: [3, 4, 1, 2]
Final Values: [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
Thierry has provided a very comprehensive explanation of why your code doesn't work as you expect. As such it is the best answer to your question.I have added my answer just as an example of you you can code this in a less complex way .
create the 2d list with the first index as list of numbers. for each iteration take the last index of temp and slice from index 1 to the end then add on index 0.
then return the list
def numbers(list_of_numbers):
temp = [list_of_numbers]
for _ in range(1, len(list_of_numbers)):
temp.append(temp[-1][1:] + temp[-1][0:1])
return temp
print(numbers([1,2,3,4]))
OUTPUT
[[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]
The problems is in shallow assignment of arrays. You should make deep copy, to really clone arrays, to make them independent.
I did it in your own code. There are a few changes of your code:
import copy that it have been added to first row.
Three usages of copy.deepcopy function instead of =(simple assignment).
import copy
def numbers(list_of_numbers):
finalValues = copy.deepcopy([list_of_numbers])
#print(list_of_numbers)
for i in range(1,len(list_of_numbers)):
print("Last array of final: ", finalValues[-1])
tempArray = copy.deepcopy(finalValues[-1])
print("Temp Array: ",tempArray)
temp = tempArray[0]
for j in range(0,len(list_of_numbers)-1):
tempArray[j] = tempArray[j+1]
tempArray[-1] = temp
finalValues.append(copy.deepcopy(tempArray))
print("Final Values: ",finalValues)
return finalValues
numbers([1,2,3,4])
Program Output
[[4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3], [4, 1, 2, 3]]
Program Output
[[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]]

Split a list into increasing sequences using itertools

I have a list with mixed sequences like
[1,2,3,4,5,2,3,4,1,2]
I want to know how I can use itertools to split the list into increasing sequences cutting the list at decreasing points. For instance the above would output
[[1, 2, 3, 4, 5], [2, 3, 4], [1, 2]]
this has been obtained by noting that the sequence decreases at 2 so we cut the first bit there and another decrease is at one cutting again there.
Another example is with the sequence
[3,2,1]
the output should be
[[3], [2], [1]]
In the event that the given sequence is increasing we return the same sequence. For example
[1,2,3]
returns the same result. i.e
[[1, 2, 3]]
For a repeating list like
[ 1, 2,2,2, 1, 2, 3, 3, 1,1,1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
the output should be
[[1, 2, 2, 2], [1, 2, 3, 3], [1, 1, 1, 2, 3, 4], [1, 2, 3, 4, 5, 6]]
What I did to achieve this is define the following function
def splitter (L):
result = []
tmp = 0
initialPoint=0
for i in range(len(L)):
if (L[i] < tmp):
tmpp = L[initialPoint:i]
result.append(tmpp)
initialPoint=i
tmp = L[i]
result.append(L[initialPoint:])
return result
The function is working 100% but what I need is to do the same with itertools so that I can improve efficiency of my code. Is there a way to do this with itertools package to avoid the explicit looping?
With numpy, you can use numpy.split, this requires the index as split positions; since you want to split where the value decreases, you can use numpy.diff to calculate the difference and check where the difference is smaller than zero and use numpy.where to retrieve corresponding indices, an example with the last case in the question:
import numpy as np
lst = [ 1, 2,2,2, 1, 2, 3, 3, 1,1,1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
np.split(lst, np.where(np.diff(lst) < 0)[0] + 1)
# [array([1, 2, 2, 2]),
# array([1, 2, 3, 3]),
# array([1, 1, 1, 2, 3, 4]),
# array([1, 2, 3, 4, 5, 6])]
Psidom already has you covered with a good answer, but another NumPy solution would be to use scipy.signal.argrelmax to acquire the local maxima, then np.split.
from scipy.signal import argrelmax
arr = np.random.randint(1000, size=10**6)
splits = np.split(arr, argrelmax(arr)[0]+1)
Assume your original input array:
a = [1, 2, 3, 4, 5, 2, 3, 4, 1, 2]
First find the places where the splits shall occur:
p = [ i+1 for i, (x, y) in enumerate(zip(a, a[1:])) if x > y ]
Then create slices for each such split:
print [ a[m:n] for m, n in zip([ 0 ] + p, p + [ None ]) ]
This will print this:
[[1, 2, 3, 4, 5], [2, 3, 4], [1, 2]]
I propose to use more speaking names than p, n, m, etc. ;-)

Removing items from a nested list Python

I am trying to remove items from a nested list in Python. I have a nested list as follows:
families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]
I want to remove the entries in each sublist that coorespond to the indexed position of the sublist in the master list. So, for example, I need to remove 0 from the first sublist, 1 from second sublist, etc. I am trying to use a list comrehension do do this. This is what I have tried:
familiesNew = [ [ families[i][j] for j in families[i] if i !=j ] for i in range(len(families)) ]
This works for range(len(families)) up to 3, however beyond that I get IndexError: list index out of range. I am not sure why. Can somebody give me an idea of how to do this. Preferably a one-liner (list comprehension).
Thanks.
You almost got it right. Just replace families[i][j] with j and it works:
>>> [ [ j for j in families[i] if i !=j ] for i in range(len(families)) ]
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]
It can be written a bit cleaner using the enumerate function:
>>> [[f for f in family if f != i] for i, family in enumerate(families)]
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]
Or even using remove if you don't mind changing the original list:
>>> for i, family in enumerate(families): family.remove(i)
Edited question, removing my answer which was solving the wrong problem. Also, added additional answer by #Ashwini:
For comparison's sake:
root# python -m timeit 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[x.remove(ind) for ind,x in enumerate(families) ]'
100000 loops, best of 3: 3.42 usec per loop
root# python -m timeit -s 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[[f for f in family if f != i] for i, family in enumerate(families)]'
100000 loops, best of 3: 4.87 usec per loop
root# python -m timeit -s 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[ filter(lambda x:x!=i,j) for i,j in enumerate(families) ]'
100000 loops, best of 3: 7.99 usec per loop
These are micro-second, so I think whatever you want to do is fine unless you are going to be doing this a lot of times.
Does this do what you want?
familiesNew=[ filter(lambda x:x!=i,j) for i,j in enumerate(families) ]
EDIT
Also note, the reason yours failed is because at the third element of the outer list ([1, 2, 3, 4, 5]) you're trying to get the fifth element in your for loop (for j in families[i] == for j in [1,2,3,4,5]), but families[i] has a length of 5, meaning the largest index is 4. Sorry if that explanation is a little unclear...perhaps the following will help clear it up a little:
families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]
def f(i,j):
print i,j,families[i]
return families[i][j]
#THIS DOES NOT WORK -- but it will tell you where it failed.
familiesNew = [ [ f(i,j) for j in families[i] if i !=j ] for i in range(len(families)) ]
If you want to modify the original list then try this:
>>>[x.remove(ind) for ind,x in enumerate(families) ]
>>>families
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]

Categories