I have a quite large numpy array of one dimension for which I would like to apply some sorting on a slice inplace and also retrieve the permutation vector for other processing.
However, the ndarray.sort() (which is an inplace operation) method does not return this vector and I may use the ndarray.argsort() method to get the permutation vector and use it to permute the slice. However, I can't figure out how to do it inplace.
Vslice = V[istart:istop] # This is a view of the slice
iperm = Vslice.argsort()
V[istart:istop] = Vslice[iperm] # Not an inplace operation...
Subsidiary question : Why the following code does not modifies V as we are working on a view of V ?
Vslice = Vslice[iperm]
Best wishes !
François
To answer your question of why assignment to view does not modify the original:
You need to change Vslice = Vslice[iperm] to Vslice[:] = Vslice[iperm] otherwise you are assigning a new value to Vslice rather than changing the values inside Vslice:
>>> a = np.arange(10, 0, -1)
>>> a
array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])
>>> b = a[2:-2]
>>> b
array([8, 7, 6, 5, 4, 3])
>>> i = b.argsort()
>>> b[:] = b[i] # change the values inside the view
>>> a # note `a` has been sorted in [2:-2] slice
array([10, 9, 3, 4, 5, 6, 7, 8, 2, 1])
Related
I have an assignment to code kfold(n, n_folds) function, which is supposed to work exactly like sklearn.model_selection.KFold: divide list of integers from 1 to n into n % k_folds groups of n // k_folds + 1 elements and n - n % k groups of n // k_folds elements (this part works well & optimized), then for each group get tuple (all integers from 1 to n not being part of the group, the group) My own realization of this last step just hits TL verdict hard (yet outputs correct answers).
def kfold(n, n_folds):
elements = np.arange(0, n)
slices = np.array(np.array_split(elements, n_folds))
ans = []
for slice in slices:
ans.append((np.setdiff1d(elements, slice), slice))
return ans
kfold(10, 3) as example returns:
[([5, 6, 7, 8, 9, 10], [1, 2, 3, 4]), ([1, 2, 3, 4, 8, 9, 10], [5, 6, 7]), ([1, 2, 3, 4, 5, 6, 7], [8, 9, 10])]
I believe the problem is my code is not fully vectorized, employing one cycle instead of numpy methods. I've read documentation of setdiff1d, setxor1d and likewise functions. While they work well dividing a single slice, I cannot see a way to make them execute all the n_folds slices simultaneously. Is there a way to make this functions work? If there is a nice alternative solution, I'd like to hear about it too
Say I have an array and I want a function to select some of its columns based on an argument a that is pre-defined :
extracted_columns = array[:,a].
If I have e.g. a = np.arange(10), I'll get the first ten columns,
What if I want to define a so that all the columns are selected without knowing the size of the array ?
I'd like to set a = : so that the function does
extracted_columns = array[:,:]
but it seems : can't pas passed as an argument. I also tried a = None but this gives me an array of dimensions 3 with the second dimension equal to 1.
Is there a nice way of doing it ?
Thanks,
Pass a slice object to your function.
MCVE:
x = np.arange(9).reshape(3, 3)
print(x)
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
a = slice(None)
print(x[:, a])
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
For your case, you'd define a function along these lines:
def foo(array, a):
return array[:, a]
And call it like this:
arr_slice = foo(array, slice(None))
I need to design a function that takes a list of int as a parameter and in that list we look at the last element it will have some value v, Then we take v cards from the top of the deck and put them above the bottom most card in the deck.
Here is an example:
>>> test_list = [1, 28, 3, 4, 27, 8, 7, 6, 5]
>>> insert_top_to_bottom(test_list)
>>> test_list = [8, 7, 6, 1, 28, 3, 4, 27, 5]
value = deck[-1] # value of the last element
I believe this will do
def insert_top_to_bottom(test_list, v):
return test_list[v : -1] + test_list[:v] + [test_list[-1]]
test_list = [1, 28, 3, 4, 27, 8, 7, 6, 5]
test_list = insert_top_to_bottom(test_list, 5)
print test_list
Here is my attempt, though you should next time show your own attempts.
Please note that I did not include any type of checking or avoiding errors. Just the basics.
def top_to_bottom(l):
last = l[len(l)-1]
move_these = l[:last]
move_these.append(l[len(l)-1])
del l[:last]
del l[len(l)-1]
l.extend(move_these)
I hope I could help.
EDIT
I didn't make it a one-line funtion so you can understand a bit better.
Since you asked, here is some more explanaition about slicing.
my_list[x:y] is a basic slice. This will output my_list from index x to (but excluding index y).
You can leave out either, which will just fill up that part as far as possible.
For example, using
my_list[:5]
will give you my_list from the beginning to (but excluding!) index 5.
If the list is [0, 1, 2, 3, 4, 5, 6], it will give [0, 1, 2, 3, 4]. If you want to get a list from a certain index until the end, you leave out the 'y' part. Like so:
my_list[3:]
Applying that on the list previously stated, you will get [3, 4, 5, 6].
I hope you understood! Comment if you have any more questions.
I am trying to write a code that changes the position of an integer inside a list (basically swaps the position with another integer)
I have tried to use all logic, but still can't understand why my code is messing up:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return (move_number)
the output should be:
[1,2,3,10,4,5,6]
>>>[1,2,3,4,10,5,6]
but output comes as:
[1,2,3,4,5,6,10]
Assuming your actual indentation looks like this:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return move_number
… the problem is that you're swapping the 10 to the right over and over in a loop, until it reaches the very end.
If that isn't what you want, why do you have the for elements in range(len(move_number)) in the first place? Just take it out, and it will only get swapped right once.
As a side note, you rarely need range(len(eggs)); you can just do for egg in eggs (or, if you need the index along with the actual object, for index, egg in enumerate(eggs)).
Also, you've got a whole lot of extra parentheses that aren't needed, and make the code harder to read.
Meanwhile, every call to index has to search the entire list to find your object's position; if you already know the position, it's better to just use it. Not only is it a lot faster, and simpler, it's also more robust—if there are two elements of the list with the same value, index can only find the first one. In your case, there's no obvious way around using index, but at least you can avoid calling it twice.
Putting that together:
SpecialNum = 10
def number_move(move_number):
x = move_number.index(SpecialNum)
y = x + 1
if y != len(move_number):
move_number[y], move_number[x] = move_number[x], move_number[y]
Finally, I said there's no obvious way around using index… but is there a non-obvious way? Sure. If you're going to call index repeatedly on the same object, we can make the last-found index part of the interface to the function, or we can even store a cache inside the function. The simplest way to do this is to turn the whole thing into a generator. A generator that mutates its arguments can be kind of confusing, so let's make it return copies instead. And finally, to make it customizable, let's take a parameter so you can specify a different SpecialNum than 10.
SpecialNum = 10
def number_move(move_number, special_num=SpecialNum):
for x, element in reversed(list(enumerate(move_number))):
if element == special_num:
while x+1 < len(move_number):
move_number = (move_number[:x] +
[move_number[x+1], move_number[x]] +
move_number[x+2:])
yield move_number
x += 1
Now, it'll move all of the 10s to the end, one step at a time. Like this:
>>> n = [1, 10, 2, 3, 10, 4, 5, 6]
>>> for x in number_move(n):
... print(x)
[1, 10, 2, 3, 4, 10, 5, 6]
[1, 10, 2, 3, 4, 5, 10, 6]
[1, 10, 2, 3, 4, 5, 6, 10]
[1, 2, 10, 3, 4, 5, 6, 10]
[1, 2, 3, 10, 4, 5, 6, 10]
[1, 2, 3, 4, 10, 5, 6, 10]
[1, 2, 3, 4, 5, 10, 6, 10]
[1, 2, 3, 4, 5, 6, 10, 10]
[1, 2, 3, 4, 5, 6, 10, 10]
You don't need the for loop :)
def number_move(move_number):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
Alternative:
>>> def number_move(m, i):
num = m.pop(i)
m.insert(i+1, num)
return m
>>> l = number_move([1,2,3,10,4,5,6], 3)
>>> l
[1, 2, 3, 4, 10, 5, 6]
I'm working with a mapping from values of a python dictionary into a numpy array like this:
import numpy as np
my_array = np.array([0, 1, 2, 3, 4, 5, 6])
my_dict = {'group_a':my_array[0:3], 'group_b':my_array[3:]}
This offers the values referenced through the dict to reflect any changes made in the full array. I need the size of the groups within the dict to be flexible. However when a group is only a single element, such as:
my_dict2 = {'group_a':my_array[0], 'group_b':my_array[1:]}
...then numpy seems to be returning the element value rather than a pointer. The value in the dict no longer reflects any changes in the array. Is there a way to clarify that I want the pointer even for a single element reference?
There is no way to do this that I know of, probably the easiest workaround is to just have the value in the dictionary be a single-element list like so:
my_dict2 = {'group_a':my_array[0:1], 'group_b':my_array[1:]}
ie,
In [2]: my_array = np.array([0, 1, 2, 3, 4, 5, 6])
In [3]: my_dict2 = {'group_a': my_array[0:1], 'group_b': my_array[1:]}
In [4]: my_dict2
Out[4]: {'group_a': array([0]), 'group_b': array([1, 2, 3, 4, 5, 6])}
In [5]: my_array[0] = 200
In [6]: my_dict2
Out[6]: {'group_a': array([200]), 'group_b': array([1, 2, 3, 4, 5, 6])}