How to extract multiple slices in an array?

Question:

I need to extract data from multiple positions in an array.

A simple array would be:-

listing = (4, 22, 24, 34, 46, 56)

I am familiar with slicing. For instance:-

listing[0:3]

would give me:-

(4, 22, 24)

However I am unable to get out multiple slices. For instance:-

listing[0:3, 4:5]

gives me

TypeError: tuple indices must be integers not tuples

Despite Searching two Python books and the Internet I cannot work out the syntax to use.

Asked By: OldSteve

||

Answers:

You can slice twice and join them.

listing[0:3] + listing[4:5]
Answered By: alex

Try:

>>> listing = (4, 22, 24, 34, 46, 56)
>>> listing[0:3], listing[4:5]
((4, 22, 24), (46,))
Answered By: Harsha Biyani

If you have the index numbers of the slices you need you can just grab them with a loop contained in a list.

index_nums = [0,2,4]
output = [listing[val] for val in index_nums]

This will return [4,24,46]

Answered By: nrc

I needed this exact construct for a pandas situation. I used a helper generator.

Python 3.7.4 (v3.7.4:e09359112e, Jul  8 2019, 14:54:52) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> listing = (4, 22, 24, 34, 46, 56)
>>> def multislice(a,sl):
...     for si in sl:
...         yield a[si]
... 
>>> list(multislice(listing,[slice(0,3),slice(4,5)]))
[(4, 22, 24), (46,)]

And by extension, how to do many various slices.

>>> list(multislice(listing,[slice(0,3),slice(4,5),slice(3,None)]))
[(4, 22, 24), (46,), (34, 46, 56)]
Answered By: Osunderdog

With a class, you can do this

class Listing():
    def __init__(self, *args):
        self.array = args
    def __getitem__(self, slices):
        return sum((self.array[s] for s in slices), ())

listing = Listing(4, 22, 24, 34, 46, 56)
listing[0:3, 4:5]   # (4, 22, 24, 46)

The constructs sum((...), ()) joins the tuples (()+()+()) and thus flattens the output.

update

A version that returns a list instead of a tuple, and that handles both single index and slices (e.g. [0] or [0:1])

class Listing(list):
    def __getitem__(self, s):
        get = super(Listing, self).__getitem__
        return sum(map(get,s), []) if hasattr(s,'__iter__') else get(s)

listing = Listing([4, 22, 24, 34, 46, 56])
listing[0:3, 4:5]   # [4, 22, 24, 46]
Answered By: Friedrich

This is a slightly improved version of Friedrich’s amazing answer. Here it is:

class Advanced_List():
  def __init__(self, *args, is_list = True):
    self.array = list(args) if is_list else args
    self.islist = is_list
  def __iter__(self):
    yield from self.array
  def __getitem__(self, slices):
    return sum([[self.array[s]] if type(s) == int else self.array[s] for s in slices], []) if type(slices) == tuple else self.array[slices] #No more multidimensional shorthand!
  def __str__(self):
    return str(self.array)
  def __repr__(self):
    return str(self)[1:-1]
  #Look at the __getitem__ method

EDIT 1:

Changed the __getitem__ line. Now, no comma is needed for a single slice!

Answered By: Someone

When working numpy arrays, you can use np.r_ to generate indices

>>> np.array([4, 22, 24, 34, 46, 56])[np.r_[0:3, 4:5]]
array([ 4, 22, 24, 46])

This can handle multiple slices of the form like 1, 0:3, :3 or -3:-1, however not properly those :, 1: or -3:, :-3 .

Answered By: Friedrich

You could use the operator.itemgetter() function along with the built-in slice class to do something very succinct and readable along these lines:

from operator import itemgetter

listing = (4, 22, 24, 34, 46, 56)

items = itemgetter(slice(0,3), slice(4,5))(listing)

print(items)  # -> ((4, 22, 24), (46,))
Answered By: martineau

Elaborating on Advanced_List above, I’d like to propose a type-checked version:
Notice that slicing returns an instance of the same, so you can do things like

x=Sliceable([1,2,3,4,5,6,7,8,9])
x[1:3,3:,4:][1,2,5:][:3]
from typing import Iterable, Union, Any, Tuple

class Sliceable():
    """ A multi-sliceable wrapper for iterables """
    def __init__(self, array: Union[Iterable[Any],Any]):
        try:
            self.array = tuple(array)
        except TypeError:
            self.array=(array,)
        
    def __iter__(self):
        yield from self.array

    def __getitem__(self, slices:Union[int,slice,Tuple[Union[int,slice],...]]) -> 'Sliceable':
        if isinstance(slices,tuple):
            return self.__class__(sum(((self.array[s],) if isinstance(s,int) else self.array[s] for s in slices),()))
        return self.__class__(self.array[slices])

    def __str__(self):
        return str(self.array)

    def __repr__(self):
        return f"{self.__class__.__name__}({self.array})"
Answered By: Philippe Gregoire
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.