I have an array, let's say arr = [1, 2, 3, 4, 5, 6, 7, 8]
and another indices array: idx = [0, 3, 4, 6]
and I want to get two arrays, one is only those indices from arr: [1, 4, 5, 7]
and another one is all the rest: [2, 3, 6, 8]
Can someone help me with that? I can only think of ugly ways to do it, but it must be some function that does it elegantly.
Thanks a lot!
You could do it like this:
selected = [arr[i] for i in idx]
other = [v for i, v in enumerate(arr) if i not in idx]
If arr has no duplicates, you could also do:
other = [v for v in arr if v not in selected]
Way to do it:
a1 = [arr[x] for x in idx]
a2 = [x for x in arr if x not in a1]
With one traversal:
no, yes = both = [], []
for i, x in enumerate(arr):
both[i in idx].append(x)
Or (as commented by Chris_Rands):
yes, no = [], []
for i, x in enumerate(arr):
(yes if i in idx else no).append(x)
Though idx should either be small for this or turned into a set (same goes for the solutions in the other answers, I guess they just don't want to talk about it).
Demo:
>>> if 1:
arr = [1, 2, 3, 4, 5, 6, 7, 8]
idx = [0, 3, 4, 6]
no, yes = both = [], []
for i, x in enumerate(arr):
both[i in idx].append(x)
print('yes', yes)
print('no', no)
yes [1, 4, 5, 7]
no [2, 3, 6, 8]
There is a neat solution with numpy:
import numpy as np
arr = np.asarray([1, 2, 3, 4, 5, 6, 7, 8]) # converts your list in numpy array
idx1 = [0, 3, 4, 6]
idx2 = [1, 2, 5, 7]
arr1 = arr[idx1] # [1 4 5 7]
arr2 = arr[idx2] # [2 3 6 8]
You can use itertools for a one line solution:
import itertools
arr = [1, 2, 3, 4, 5, 6, 7, 8]
idx = [0, 3, 4, 6]
[(out_index, not_in_arr), (in_index, in_arr)] = [(a, list(b)) for a, b in itertools.groupby(sorted(arr, key=lambda x:arr.index(x) in idx), key=lambda x:arr.index(x) in idx)]
print(not_in_arr)
print(in_arr)
Output:
[2, 3, 6, 8]
[1, 4, 5, 7]
You can also map each value in arr to a dictionary, indicating if it's index is present in idx:
arr = [1, 2, 3, 4, 5, 6, 7, 8]
idx = [0, 3, 4, 6]
# converted to a set
idx_lookup = set(idx)
d = {x: True if i in idx_lookup else False for i, x in enumerate(arr)}
print(d)
Which gives this dictionay:
{1: True, 2: False, 3: False, 4: True, 5: True, 6: False, 7: True, 8: False}
I also converted idx to a set since in this case, duplicate indices are not necessary, and set/dictionary lookup is O(1). However, list lookup is O(n), so this optimization is worth it if possible.
Once you have this dictionary, you can filter out the elements you want to keep, and the rest of the elements from this:
keep = list(filter(lambda x: d[x], arr))
rest = list(filter(lambda x: not d[x], arr))
print(keep)
print(rest)
Which Outputs:
[1, 4, 5, 7]
[2, 3, 6, 8]
Note: You can also use list comprehensions above filtering of keep and rest:
keep = [x for x in arr if d[x]]
rest = [x for x in arr if not d[x]]
Related
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]
How would I split list of integers such that I would have them in sublists with minimum difference?
For example
[4,1,5,3] --> [[1], [3,4,5]], length 2
[4,2,1,3] --> [[1,2,3,4]], length 1
[5,2,7,6,3,9] --> [[2,3], [5,6], [9]], length 3
[1,2,3,4,5,6,8,9,10] --> [[1,2,3,4,5,6], [8,9,10]], length 2.
Current approach is
def split(l):
res = []
n = len(l)
l.sort()
# If the list is just consecutive integers return 1
if l == list(range(min(l), max(l)+1)):
return 1
for l0, l1 in zip(l, l[1:]):
if abs(l0 - l1) <= 1:
res.append([l0, l1])
return len(res)
this works for the first three cases well, but the last one fails... I think the problem is that in the loop I'm just conditioning by the difference of the two consecutive integers...
If you are allowed to use external packages more_itertools have split_when method which can what you want:
import more_itertools
lst = [1,2,3,4,5,6,8,9,10]
lst.sort()
splitted = list(more_itertools.split_when(lst, lambda x, y: abs(x-y) > 1))
print(splitted)
Output:
[[1, 2, 3, 4, 5, 6], [8, 9, 10]]
I would just check if the difference between 2 numbers is bigger than 1:
def mySplit(lst):
res = []
lst.sort()
subList = [lst[0]]
for i in range(1, len(lst)):
prev, cur = lst[i-1], lst[i]
if cur - prev > 1:
res.append(subList)
subList = []
subList.append(cur)
res.append(subList)
return res
tests = ([4,1,5,3], [4,2,1,3], [5,2,7,6,3,9], [1,2,3,4,5,6,8,9,10,13])
for test in tests:
print(mySplit(test))
Out:
[[1], [3, 4, 5]]
[[1, 2, 3, 4]]
[[2, 3], [5, 6, 7], [9]]
[[1, 2, 3, 4, 5, 6], [8, 9, 10], [13]]
Very fast-running and short solution using numpy:
(once install numpy using python -m pip install numpy).
Try it online!
import numpy as np
for l in [
[4,1,5,3],
[4,2,1,3],
[5,2,7,6,3,9],
[1,2,3,4,5,6,8,9,10],
]:
a = np.sort(l)
res = np.split(a, np.flatnonzero(np.abs(np.diff(a)) > 1) + 1)
print('input', l, 'sorted', a, '\n', 'result', res)
Outputs:
input [4, 1, 5, 3] sorted [1 3 4 5]
result [array([1]), array([3, 4, 5])]
input [4, 2, 1, 3] sorted [1 2 3 4]
result [array([1, 2, 3, 4])]
input [5, 2, 7, 6, 3, 9] sorted [2 3 5 6 7 9]
result [array([2, 3]), array([5, 6, 7]), array([9])]
input [1, 2, 3, 4, 5, 6, 8, 9, 10] sorted [ 1 2 3 4 5 6 8 9 10]
result [array([1, 2, 3, 4, 5, 6]), array([ 8, 9, 10])]
I have the following list:
a = [1, 2, 5, 4, 3, 6]
And I want to know how I can swap any two values at a time randomly within a list regardless of position within the list. Below are a few example outputs on what I'm thinking about:
[1, 4, 5, 2, 3, 6]
[6, 2, 3, 4, 5, 1]
[1, 6, 5, 4, 3, 2]
[1, 3, 5, 4, 2, 6]
Is there a way to do this in Python 2.7? My current code is like this:
import random
n = len(a)
if n:
i = random.randint(0,n-1)
j = random.randint(0,n-1)
a[i] += a[j]
a[j] = a[i] - a[j]
a[i] -= a[j]
The issue with the code I currently have, however, is that it starts setting all values to zero given enough swaps and iterations, which I do not want; I want the values to stay the same in the array, but do something like 2opt and only switch around two with each swap.
You are over-complicating it, it seems. Just randomly sample two indices from the list, then swap the values at those indicies:
>>> def swap_random(seq):
... idx = range(len(seq))
... i1, i2 = random.sample(idx, 2)
... seq[i1], seq[i2] = seq[i2], seq[i1]
...
>>> a
[1, 2, 5, 4, 3, 6]
>>> swap_random(a)
>>> a
[1, 2, 3, 4, 5, 6]
>>> swap_random(a)
>>> a
[1, 2, 6, 4, 5, 3]
>>> swap_random(a)
>>> a
[1, 2, 6, 5, 4, 3]
>>> swap_random(a)
>>> a
[6, 2, 1, 5, 4, 3]
Note, I used the Python swap idiom, which doesn't require an intermediate variable. It is equivalent to:
temp = seq[i1]
seq[i1] = seq[i2]
seq[i2] = temp
Let's say I'm not allowed to use libraries.
How do I go about calculating the product of indexes in a list.
Let's assume none of the integers are 0 or less.
The problem gets harder as I'm trying to calculate the indexes vertically.
bigList = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
With numpy the solution for my problem would be:
import numpy as np
print([np.prod(l) for l in zip(*bigList)])
[1, 32, 243, 1024, 3125]
However without it my solution is much more chaotic:
rotateY = [l for l in zip(*bigList)]
productList = [1]* len(bigList)
count = 0
for l in rotateY:
for i in l:
productList[count] *= i
count += 1
print(productList)
[1, 32, 243, 1024, 3125]
You can iterate over each row getting each row's n-th element, and multiplying each element together:
>>> from functools import reduce
>>>
>>> def mul_lst(lst):
return reduce(lambda x, y: x * y, lst)
>>>
>>> bigList = [[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
>>>
>>> [mul_lst([row[i] for row in bigList]) for i in range(len(bigList))]
[1, 32, 243, 1024, 3125]
If you cannot use any libraries, including functools, you can write the logic for the mul_lst function manually:
>>> def mul_lst(lst):
product = lst[0]
for el in lst[1:]:
product *= el
return product
>>> mul_lst([3, 3])
9
>>> mul_lst([2, 2, 2, 2, 2])
32
And why not simply:
productList = []
for i in range(len(bigList[0]):
p = 1
for row in bigList:
p *= row[i]
productList.append(p)
Alternatively, a small improvement over your solution:
productList = [1]* len(bigList[0])
for row in bigList:
for i, c in enumerate(row):
productList[i] *= c
We can transpose the nested list and then use reduce (a Python built-in) in Python 2.x on each element (list) for a one-liner -
>>> [reduce(lambda a,b: a*b, i) for i in map(list, zip(*bigList))]
[1, 32, 243, 1024, 3125]
Here's a quick recursive solution
def prod(x):
""" Recursive approach with behavior of np.prod over axis 0 """
if len(x) is 1:
return x
for i, a_ in enumerate(x.pop()):
x[0][i] *= a_
return prod(x)
i have been working on this for 3 hours now but i have no clue how to do it
can anyone help me with this?
values = [1, 2, 3, 4, 5]
temp = values[0]
for index in range (len(values) -1):
values[index] = values [index]
values[len(values)-1] = temp
print values
i want the printed values to be in order as [2,3,4,5,1]
by simply changing those in the brackets
deque is more efficient way to do
In [1]: import collections
In [3]: dq = collections.deque([1,2,3,4,5])
In [4]: dq.rotate(-1)
In [5]: dq
Out[5]: deque([2, 3, 4, 5, 1])
What you are trying to achieve is not available in the python libraries but you can leverage slicing to rotate list
Implementation
def rotate(seq, n = 1, direc = 'l'):
if direc.lower() == 'l':
seq = seq[n:] + seq[0:n]
else:
seq = seq[-n:] + seq[:-n]
return seq
Demonstration
>>> rotate(values)
[2, 3, 4, 5, 1]
>>> rotate(values,2,'l')
[3, 4, 5, 1, 2]
>>> rotate(values,2,'r')
[4, 5, 1, 2, 3]
Simple but powerful slice syntax:
values = [1, 2, 3, 4, 5]
shifted = values[1:]+values[:1]
assert shifted == [2, 3, 4, 5, 1]
How about time one-liner, in the spirit of sleepsort:
while values != [2, 3, 4, 5, 1]: random.shuffle(values)