13

I have a 1-D numpy array a = [1,2,3,4,5,6] and a function that gets two inputs, starting_index and ending_index, and returns a[staring_index:ending_index].

Clearly I run into trouble when ending_index is smaller than starting_index. In this case, the function should start from the starting_index and traverse the vector a in a circular way, i.e., return all the elements coming after starting_index plus all the elements from index zero to ending_index.

For instance, if starting_index=4 and ending_index=1 then the output should be output = [5,6,1]. I can implement it with an if condition but I was wondering if there is any Pythonic and concise way to do it?

1

4 Answers 4

11

np.take has a wrap mode:

In [171]: np.take(np.arange(1,7),range(4,7),mode='wrap')
Out[171]: array([5, 6, 1])

That's not quite what you want.

Actually, modulus does the same thing

In [177]: a[np.array([4,5,6])%6]
Out[177]: array([5, 6, 1])

But how about a small function that turns (4,1) into [4, 5, 6], or if you prefer [4, 5, 0]?

def foo(a, start, stop): 
    # fn to convert your start stop to a wrapped range
    if stop<=start:
        stop += len(a)
    return np.arange(start, stop)%len(a)

a[foo(a,4,1)]  # or
np.take(a,foo(a,4,1))
Sign up to request clarification or add additional context in comments.

1 Comment

Shouldn't it be modulo in the return statement of method foo for this to work?
6

Unfortunatly you cannot do this with slicing, you'll need to concatonate to two segments:

import numpy as np

a = [1, 2, 3, 4, 5, 6]
if starting_index > ending_index:
    part1 = a[start_index:]
    part2 = a[:end_index]
    result = np.concatenate([part1, part2])
else:
    result = a[start_index:end_index]

3 Comments

currently i'm doing something like this. Thanks. though, can't be used with numpy, would you explain more about slicing? I assume it can be used with lists?
Numpy indexing and slicing is described here there is also a more general discussion of slicing here.
I can confirm that this is faster than using np.roll, especially for smaller (<1e6) size arrays. E.g. for a 100 item array: np.roll: 23.4 µs ± 968 ns per loop; np.concatenate: 3.22 µs ± 61 ns per loop. For an 1e6 item array concatenate is still marginally faster: 2.19 ms ± 82 µs vs 2.29 ms ± 92.9 µs per loop. Using scalar as in this example is also faster than using range or an index array for larger arrays
2

An alternative that you can use is the numpy roll function combined with indexing:

# -*- coding: utf-8 -*-
import numpy as np

def circular_array(starting_index, ending_index):

    idx = np.arange(1,7)
    idx = np.roll(idx, -starting_index)[:(len(idx)-starting_index+ending_index)%len(idx)]

    return idx


a = circular_array(4, 1)
print a

Comments

0

This circles forever.

def circular_indices(lb, ub, thresh):
    indices = []
    while True:
        stop = min(ub, thresh)
        ix = np.arange(lb, stop)
        indices.append(ix)
        if stop != ub:
            diff = ub - stop
            lb = 0
            ub = diff
        else:
            break

    return np.concatenate(indices)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.