Related
I have been using the sympy permutations package. So far I have declared permutations as follows
from sympy.combinatorics.generators import symmetric, Permutation
p = Permutation([[2, 3], [5]])
print(p(([0, 1, 2, 3, 4, 5]))
out:[0, 1, 3, 2, 4, 5]
I would like to declare a permutation given 2 lists. For example, I would like the element
I would like the permutation to act on integers rather than positions in the list (so that (01) * [1, 2, 3, 0] = [0, 2, 3, 1], instead of (01) * [1, 2, 3, 0] = [2, 1, 3, 0])
How can I do this ?
See the discussion about composition in permutations.py:
>>> start = Permutation([1, 0, 2, 3])
>>> finish = Permutation([2, 3, 1, 0])
>>> p = finish*start
>>> p([1,0,2,3])
[2, 3, 1, 0]
>>> p
Permutation(0, 2)(1, 3)
Maybe a better way to do this, but this seems to work if you want to access by name instead of position:
from sympy.combinatorics import Permutation
from sympy.utilities.iterables import permutations
P = Permutation
start = [1,0,2,3]
end = [2,3,1,0]
do = P(end)*P(start)
def byname(l):
a = do([l.index(i) for i in range(len(l))])
return [a.index(i) for i in range(len(l))]
for i in permutations(range(4)):
i, byname(i)
I have looked for similar discussions, but I haven't been able to find any - so here goes:
Basically, I want to have a list, say, preallocated to 10 values:
mylist = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
Then, I want to define, say, two sets of values, which I expect, the mylist will be "switching" between in my code most of the time; say:
valsA = ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
valsB = ( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )
Since these are predefined sets of values, no point in defining them as lists, so they are defined as tuples.
Now, I could in principle just do:
# some code running here ...
# switch mylist to valsA:
mylist = valsA
# some more code using mylist running here ...
# switch mylist to valsB:
mylist = valsB
# some more code using mylist running here ...
... however, the problem is, that just by direct assignment, mylist then becomes identifier for the tuple:
>>> valsA = ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 )
>>> valsB = ( 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 )
>>> mylist = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
>>> id(valsA)
123145300863808
>>> hex(id(valsA))
'0x6fffffe9eb40'
>>> hex(id(valsB))
'0x6fffffeb81c0'
>>> hex(id(mylist))
'0x6fffffea9bc0'
>>> mylist = valsA
>>> hex(id(mylist))
'0x6fffffe9eb40'
>>> type(mylist)
<class 'tuple'>
... and so, after such an assignment, if my code wants to change a single slot/item/entry in mylist, it won't be able to, since it is now an immutable tuple:
>>> mylist
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> mylist[2] = 15
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
So, instead, what I'm looking for an efficient way to copy the values of the tuple - say, the most obvious approach for me would be:
>>> mylist = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] # reset
>>> for tidx, tval in enumerate(mylist): mylist[tidx] = valsA[tidx]
...
>>> print(mylist)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
... which then allows for changing individual items in mylist:
>>> mylist[2] = 15
>>> print(mylist)
[0, 1, 15, 3, 4, 5, 6, 7, 8, 9]
... however, then I have a Python-level loop I have to go through, which from earlier experience, is not necessarily the fastest way to do things; and also, it is not necessarily the most readable (although I could always wrap it in a function/method - but then I have an overhead of function calls).
Is there a more efficient way to copy values from a tuple to a list, than running a for loop? Say, maybe there is something like (pseudocode) mylist.fill(valsA) method, or some other approach?
Thanks to the comment by #Amongalen, from the linked question, I recalled "casting" to list, that is mylist = list(valsA); but I was not sure whether it is faster than just a loop; I just tested that, and it seems that it is:
>>> import timeit
>>> timeit.timeit("mylist = list(valsB)", number=10000, globals=globals())
0.001488900000367721
>>> timeit.timeit("for tidx, tval in enumerate(mylist): mylist[tidx] = valsA[tidx]", number=10000, globals=globals())
0.009989700000005541
So, yeah, mylist = list(valsA) is faster - and I guess, it is most efficient to switch between values in this way; although as caveat, the ID of the object mylist points to, will change.
So, I know I can get a random list from a population using the random module,
l = [0, 1, 2, 3, 4, 8 ,9]
print(random.sample(l, 3))
# [1, 3, 2]
But, how do I get the list of the unselected ones? Do, I need to remove them manually from the list? Or, is there a method to get them too?
Edit: The list l from example doesn't contain the same items multiple times, but when it does I wouldn't want it removed more than it's selected as sample.
l = [0, 1, 2, 3, 4, 8 ,9]
s1 = set(random.sample(l, 3))
s2 = set(l).difference(s1)
>>> s1
{0, 3, 8}
>>> s2
{1, 2, 4, 9}
Update: same items multiple times
You can shuffle your list first and partition your population after in two:
l = [7, 4, 5, 4, 5, 9, 8, 6, 6, 6, 9, 8, 6, 3, 8]
pop = l[:]
random.shuffle(pop)
pop1, pop2 = pop[:3], pop[3:]
>>> pop1
[8, 4, 9]
>>> pop2
[7, 6, 8, 6, 5, 6, 9, 6, 5, 8, 4, 3]
Because your list can contain multiple same items, you can change to the approach below:
import random
l = [0, 1, 2, 3, 4, 8 ,9]
random.shuffle(l)
selected = l[:3]
unselected = l[3:]
print(selected)
# [4, 0, 1]
print(unselected)
# [8, 2, 3, 9]
If you want to keep track of duplicates, you could count the items of each type and compare the population count to the sample count.
If you don't care about the order of items in the population, you could do it like this:
from collections import Counter
import random
population = [1, 1, 2, 2, 9, 7, 9]
sample = random.sample(population, 3)
pop_count = Counter(population)
samp_count = Counter(sample)
unsampled = [
k
for k in pop_count
for i in range(pop_count[k] - samp_count[k])
]
If you care about the order in the population, you could do something like this:
check = sample.copy()
unsampled = []
for val in population:
if val in check:
check.remove(val)
else:
unsampled.append(val)
Or there's this weird list comprehension (not recommended):
check = sample.copy()
unsampled = [
x
for x in population
if x not in check or check.remove(x)
]
The if clause here uses two tricks:
both parts of the test will be Falseish if x is not in check (list.remove() always returns None), and
remove() will only be called if the first part fails, i.e., if x is in check.
Basically, if (and only if) x is in check, it will bomb through and check the next condition, which will also be False (None), but will have the side effect of removing one copy of x from check.
You can do with:
import random
l = [0, 1, 2, 3, 4, 8 ,9]
rand = random.sample(l, 3)
rest = list(set(l) - set(rand))
print(f"initial list: {l}")
print(f"random list: {rand}")
print (f"rest list: {rest}")
Result:
initial list: [0, 1, 2, 3, 4, 8, 9]
random list: [2, 9, 0]
rest list: [8, 1, 3, 4]
If I had a list that ranged from 0 - 9 for example. How would I use the random.seed function to get a random selection from that range of numbers? Also how I define the length of the results.
import random
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = 10
random.seed(a)
length = 4
# somehow generate random l using the random.seed() and the length.
random_l = [2, 6, 1, 8]
Use random.sample. It works on any sequence:
>>> random.sample([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)
[4, 2, 9, 0]
>>> random.sample('even strings work', 4)
['n', 't', ' ', 'r']
As with all functions within the random module, you can define the seed just as you normally would:
>>> import random
>>> lst = list(range(10))
>>> random.seed('just some random seed') # set the seed
>>> random.sample(lst, 4)
[6, 7, 2, 1]
>>> random.sample(lst, 4)
[6, 3, 1, 0]
>>> random.seed('just some random seed') # use the same seed again
>>> random.sample(lst, 4)
[6, 7, 2, 1]
>>> random.sample(lst, 4)
[6, 3, 1, 0]
import random
list = [] # your list of numbers that range from 0 -9
# this seed will always give you the same pattern of random numbers.
random.seed(12) # I randomly picked a seed here;
# repeat this as many times you need to pick from your list
index = random.randint(0,len(list))
random_value_from_list = list[index]
If you got numpy loaded, you can use np.random.permutation. If you give it a single integer as argument it returns a shuffled array with the elements from np.arange(x), if you give it a list like object the elements are shuffled, in case of numpy arrays, the arrays are copied.
>>> import numpy as np
>>> np.random.permutation(10)
array([6, 8, 1, 2, 7, 5, 3, 9, 0, 4])
>>> i = list(range(10))
>>> np.random.permutation(i)
array([0, 7, 3, 8, 6, 5, 2, 4, 1, 9])
This question already has answers here:
Repeating elements of a list n times
(14 answers)
Closed 5 months ago.
I want to write a function that reads a list [1,5,3,6,...]
and gives [1,1,5,5,3,3,6,6,...].
Any idea how to do it?
>>> a = range(10)
>>> [val for val in a for _ in (0, 1)]
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
N.B. _ is traditionally used as a placeholder variable name where you do not want to do anything with the contents of the variable. In this case it is just used to generate two values for every time round the outer loop.
To turn this from a list into a generator replace the square brackets with round brackets.
>>> a = [1, 2, 3]
>>> b = []
>>> for i in a:
b.extend([i, i])
>>> b
[1, 1, 2, 2, 3, 3]
or
>>> [a[i//2] for i in range(len(a)*2)]
[1, 1, 2, 2, 3, 3]
numpy.repeat does what you want:
import numpy as np
yourList = [1,5,3,6]
n = 2
list(np.repeat(yourList, n))
result:
[1, 1, 5, 5, 3, 3, 6, 6]
If you don't mind using numpy arrays you can also omit the list() call in the last line.
If you already have the roundrobin recipe described in the documentation for itertools—and it is quite handy—then you can just use
roundrobin(my_list, my_list)
I would use zip and itertools.chain.
>>> import itertools
>>> l = [1,5,3,6,16]
>>> list(itertools.chain(*zip(l,l)))
[1, 1, 5, 5, 3, 3, 6, 6, 16, 16]
Note: I only used list to consume the generator to make it fit for printing. You probably don't need the list call in your code...
It is possible use list multiplication. Case you need each list member together just use sorted method.
>>> lst = [1,2,3,4]
>>> sorted(lst*2)
[1,1,2,2,3,3,4,4]
With a little slicing...
>>> a = [3, 1, 4, 1, 5]
>>> a[:0] = a[::2] = a[1::2] = a[:]
>>> a
[3, 3, 1, 1, 4, 4, 1, 1, 5, 5]
I would use
import itertools
foo = [1, 5, 3, 6]
new = itertools.chain.from_iterable([item, item] for item in foo)
new will be an iterator that lazily iterates over the duplicated items. If you need the actual list computed, you can do list(new) or use one of the other solutions.
One can use zip and flat the list
a = [3, 1, 4, 1, 5]
sum(zip(a,a), ()) # (3, 3, 1, 1, 4, 4, 1, 1, 5, 5)
The output is a tuple, but conversion to a list is easy.
Regarding flatting a tuple with sum see https://stackoverflow.com/a/952946/11769765 and python: flat zip.
For as much as Guido dislikes the functional operators, they can be pretty darned handy:
>>> from operator import add
>>> a = range(10)
>>> b = reduce(add, [(x,x) for x in a])
For a more general approach you could go with a list comprehension and a factor term.
Example
sample_list = [1,2,3,4,5]
factor = 2
new_list = [entry for entry in sample_list for _ in range(factor)]
Out:
>>> new_list
[1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
Changing the factor variable will change how many entry of each item in the list you will have in the new list.
You could also wrap it up in a function:
def multiply_list_entries(list_, factor = 1):
list_multiplied = [entry for entry in list_ for _ in range(factor)]
return list_multiplied
>>> multiply_list_entries(sample_list, factor = 3)
[1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5]
ls1=[1,2,3]
ls2=[]
for i in ls1:
ls2.append(i)
ls2.append(i)
This code duplicates each elements in ls1
the result ls2 --> [1,1,2,2,3,3]