Hey everyone how can I sort array index to index.
So I have code here
a = [0, 1, 2, 3, 4, 4, 3, 2, 1, 0, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4]
how can i sort to?
[0, 4, 1, 3, 2, 2, 3, 1, 4, 0, 4, 0, 3, 1, 2, 2, 1, 3, 0, 4]
this is my idea
I could be wrong, but it sounds like you would like to return a list that is sorted like this:
[first_item, last_item, second_item, second_to_last_item, third_item, third_to_last_item,...]
I don't know of a one-line way to do that, but here's one way you could do it:
import numpy as np
a = [0, 1, 2, 3, 7] # length of list is an odd number
# create indexes that are all positive
index_values = np.repeat(np.arange(0, len(a)//2 + 1), 2) # [0,0,1,1,.....]
# make every other one negative
index_values[::2] *= -1 #[-0, 0, -1, 1, ....]
# return a[i]
[a[i] for i in index_values[1:(len(a)+1)]]
### Output: [0, 7, 1, 3, 2]
It also works for lists with even length:
a = [0, 1, 2, 3, 7, 5] # list length is an even number
index_values = np.repeat(np.arange(0, len(a)//2 + 1), 2) # [0,0,1,1,.....]
index_values[::2] *= -1 #[-0, 0, -1, 1, ....]
[a[i] for i in index_values[1:(len(a)+1)]]
### Output: [0, 5, 1, 7, 2, 3]
Here’s an almost one liner (based on #Callin’s sort method) for those that want one and that can’t/don’t want to use pandas:
from itertools import zip_longest
def custom_sort(a):
half = len(a)//2
return [n for fl in zip_longest(a[:half], a[:half-1:-1]) for n in fl if n is not None])
Examples:
custom_sort([0, 1, 2, 3, 7])
#[0, 7, 1, 3, 2]
custom_sort([0, 1, 2, 3, 7, 5])
#[0, 5, 1, 7, 2, 3]
This can be done in one line, although you’d be repeating the math to find the halfway point
[n for x in zip_longest(a[:len(a)//2], a[:(len(a)//2)-1:-1]) for n in x if n is not None]
Sometimes we want to sort in place, that is without creating a new list. Here is what I came up with
l=[1,2,3,4,5,6,7]
for i in range(1, len(l), 2):
l.insert(i, l.pop())
Related
I have a numpy array like the following:
A = np.array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 5, 1, 1, 1],
[1, 1, 1, 1, 3, 3, 1, 1],
[1, 1, 1, 1, 1, 1, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 4, 1, 1]])
I am looking for the minimum indices in each column. I found this using numpy.argmin as follows:
I = np.zeros(A.shape[1], dtype=np.int64)
for j in range(A.shape[1]):
I[j] = np.argmin(A[:, j])
This gives me I = [0, 0, 0, 0, 0, 0, 0, 0]. Since there are ties, I could obtain the following: I = [0, 1, 2, 3, 4, 0, 5, 1], where I break the ties by the index that is least used (from the previous indices).
In more details:
For j=0, we have np.argmin(A[:, 0]) in [0, 1, 2, 3, 4, 5] and, say, we choose np.argmin(A[:, 0]) = 0.
For j=1, we have np.argmin(A[:, 1]) in [0, 1, 2, 3, 4, 5] and we have to choose the minimum index from [1, 2, 3, 4, 5] since these indices are the least used (we already choose np.argmin(A[:, 0]) = 0 for j=0). Say, we choose np.argmin(A[:, 1])=1.
For j=2, we have np.argmin(A[:, 2]) in [0, 1, 2, 3, 4, 5] and we have to choose the minimum index from [2, 3, 4, 5] since these indices are the least used.
We continue in this way...
For j=5, we have np.argmin(A[:, 5]) in [0, 1, 3, 4] and we have to choose the minimum index from [0, 1, 3, 4] since these indices are the least used. Say we choose np.argmin(A[:, 5])=0.
For j=6, we have np.argmin(A[:, 6]) in [0, 1, 2, 4, 5] and we have to choose from [5] since these indices are the least used. We choose np.argmin(A[:, 6])=5.
For j=7, we have np.argmin(A[:, 7]) in [0, 1, 2, 3, 4, 5] and we have to choose from [1, 2, 3, 4, 5] since these indices are the least used. Say we choose np.argmin(A[:, 7])=1.
I hope it is clear. My question is how to find the minimum indices and break ties by the least used indices in Python?
You could use min combined with a dictionary for keeping the counts of each index:
import numpy as np
A = np.array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 5, 1, 1, 1],
[1, 1, 1, 1, 3, 3, 1, 1],
[1, 1, 1, 1, 1, 1, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 4, 1, 1]])
counts = {}
I = np.zeros(A.shape[1], dtype=np.int64)
for j in range(A.shape[1]):
_, _, i = min([(v, counts.get(i, 0), i) for i, v in enumerate(A[:, j])])
counts[i] = counts.get(i, 0) + 1
I[j] = i
print(I)
Output
[0 1 2 3 4 0 5 1]
The idea is to create the following key: (value, count of index, index), and then use the normal comparison of tuples, so if the values are equal the one with less counts of the corresponding index will be selected, if both counts are equal get the one with lower index will be selected.
I have a 1d array with zeros scattered throughout. Would like to create a second array which contains the position of the last zero, like so:
>>> a = np.array([1, 0, 3, 2, 0, 3, 5, 8, 0, 7, 12])
>>> foo(a)
[0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3]
Is there a built-in NumPy function or broadcasting trick to do this without using a for loop or other iterator?
>>> (a == 0).cumsum()
array([0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3])
I want to create new list according cumulative sums of numbers in a list. Input is ideal - can be splitting to subset, sum of each subset is equal. Length of subset is not equal. Number of subset is input.
Each subset of output represents increment integers [0,1,2,3,...], which replace original input. Quantity of integers is number of subsets.
Example:
number of subsets = 2
input = [1, 4, 5]
#cumsum = [1, 5, 10]
subsets = [1,5], [10]
output-subsets = [0,0], [1]
output = [0, 0, 1]
Example1:
number of subsets = 4
input = [1, 2, 3, 4, 2, 5, 1, 6]
#cumsum = [1, 3, 6, 10, 12, 17, 18, 24]
subsets = [1,3,6], [10, 12],[17, 18], [24]
output-subsets = [0, 0, 0], [1, 1], [2, 2], [3]
output = [0, 0, 0, 1, 1, 2, 2, 3]
number of subsets = 2
input = [1, 2, 3, 4, 2, 5, 1, 6]
#cumsum = [1, 3, 6, 10, 12, 17, 18, 24]
subsets = [1, 3, 6, 10, 12],[17, 18, 24]
output-subsets = [0, 0, 0, 0, 0], [1, 1, 1]
output = [0, 0, 0, 0, 0, 1, 1, 1]
I try modified SO question:
def changelist(lis, t):
total = 0
s = sum(lis)
subset = s/t
for x in lis:
total += x
i= 1
if(total <= subset):
i = 0
yield i
#changelist([input array], number of subset)
print list(changelist([1, 2, 3, 4, 2, 5, 1, 6], 4))
but only first subset is correct:
output = [0, 0, 0, 1, 1, 1, 1, 1]
I think numpy.array_split is problematic strange behaviour of numpy array_split.
I would really love any kind of explanation or help.
This should solve your problem:
def changelist (l, t):
subset = sum(l) / t
current, total = 0, 0
for x in l:
total += x
if total > subset:
current, total = current + 1, x
yield current
Examples:
>>> list(changelist([1, 4, 5], 2))
[0, 0, 1]
>>> list(changelist([1, 2, 3, 4, 2, 5, 1, 6], 4))
[0, 0, 0, 1, 1, 2, 2, 3]
>>> list(changelist([1, 2, 3, 4, 2, 5, 1, 6], 2))
[0, 0, 0, 0, 0, 1, 1, 1]
How does it work?
current stores the "id" of the current subset, total the sum of the current subset.
For each element x in your initial list l, you add its value to the current total, if this total is greater than the expected sum of each subset (subset in my code), then you know that you are in the next subset (current = current + 1) and you "reset" the total of the current subset to the actuel element (total = x).
You can use NumPy here after converting the input to an array for a vectorized solution, assuming N as the number of subsets, as listed here -
def modified_cumsum(input,N):
A = np.asarray(input).cumsum()
return np.append(False,np.in1d(A,(1+np.arange(N))*A[-1]/N))[:-1].cumsum()
Sample runs -
In [31]: N = 2 #number of subsets
...: input = [1, 4, 5]
...:
In [32]: modified_cumsum(input,N)
Out[32]: array([0, 0, 1])
In [33]: N = 4 #number of subsets
...: input = [1, 2, 3, 4, 2, 5, 1, 6]
...:
In [34]: modified_cumsum(input,N)
Out[34]: array([0, 0, 0, 1, 1, 2, 2, 3])
In [35]: N = 2 #number of subsets
...: input = [1, 2, 3, 4, 2, 5, 1, 6]
...:
In [36]: modified_cumsum(input,N)
Out[36]: array([0, 0, 0, 0, 0, 1, 1, 1])
I know if I want to create a list like this:
[0 1 2 0 1 2 0 1 2 0 1 2]
I can use this command:
range(3) * 4
Is there a similar way to create a list like this:
[0 0 0 0 1 1 1 1 2 2 2 2]
I mean a way without loops
Integer division can help:
[x/4 for x in range(12)]
Same thing through map:
map(lambda x: x/4, range(12))
In python 3 integer division is done with //.
Beware that multiplication of a list will likely lead to a result you probably don't expect.
Yes, you can.
>>> [e for e in range(3) for _ in [0]*4]
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]
itertools module is always an option:
>>> from itertools import chain, repeat
>>> list(chain(repeat(0, 4), repeat(1, 4), repeat(2, 4)))
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]
More general way is:
def done(group_count, repeat_count):
return list(chain(*map(lambda i: repeat(i, repeat_count),
range(group_count))))
>>> done(3, 4)
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]
Without any explicit "for" :)
>>> list(chain(*zip(*([range(5)] * 5))))
[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4]
What about this:
>>> sum([ [x]*4 for x in range(5)],[])
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]
>>>
or
>>> reduce(lambda x,y: x+y, [ [x]*4 for x in range(5)])
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]
>>>
If you can't use a loop in your current method, create one in an other?
range(0,1)*4 + range(1,2)*4 + range(2,3)*4
Everybody knows that a list of numbers can be obtained with range like this;:
>>> list(range(5))
[0, 1, 2, 3, 4]
If you want, say, 3 copies of each number you could use:
>>> list(range(5)) * 3
[0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
But is there an easy way using range to repeat copies like this instead?
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
Examples:
sorted(list(range(5)) * 3) # has unnecessary n * log(n) complexity
[x//3 for x in range(3*5)] # O(n), but division seems unnecessarily complicated
You can do:
>>> [i for i in range(5) for _ in range(3)]
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
the range(3) part should be replaced with your number of repetitions...
BTW, you should use generators
Just to make it clearer, the _ is a variable name for something you don't care about (any name is allowed).
This list comprehension uses nested for loops and are just like that:
for i in range(5):
for j in range(3):
#your code here
Try this:
itertools.chain.from_iterable(itertools.repeat(x, 3) for x in range(5))
from itertools import chain, izip
list(chain(*izip(*[xrange(5)]*3)))
Gives
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
Leave off the list and you have a generator.
EDIT: or even better (leaves out a function call to izip):
list(chain(*([x]*3 for x in xrange(5))))
There is a very simple way to do this with a help from numpy. Example:
>>> import numpy as np
>>> np.arange(5*3) // 3
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])
With range you can do the following:
>>> list(map(lambda x: x // 3, range(5*3)))
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
Remembering that // performs a strict integer division.
>>> from itertools import chain, izip, tee
>>> list(chain.from_iterable(izip(*tee(range(5), 3))))
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
A cool iterator using another approach:
>>> from collections import Counter
>>> Counter(range(5) * 3).elements()
I like to Keep It Simple :)
>>> sorted(list(range(5)) * 3)
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4]
import itertools
[x for tupl in itertools.izip(*itertools.tee(range(0,5),3)) for x in tupl]
Or:
[x for tupl in zip(range(0,5), range(0,5), range(0,5)) for x in tupl]