Reputation: 608
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.
Upvotes: 48
Views: 51282
Reputation: 3754
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.
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]
Upvotes: 7
Reputation: 36
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})"
Upvotes: 0
Reputation: 123483
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,))
Upvotes: 1
Reputation: 3754
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
.
Upvotes: 1
Reputation: 221
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
Changed the __getitem__
line. Now, no comma is needed for a single slice!
Upvotes: 2
Reputation: 19
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)]
Upvotes: 0
Reputation: 143
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]
Upvotes: 11
Reputation: 7268
Try:
>>> listing = (4, 22, 24, 34, 46, 56)
>>> listing[0:3], listing[4:5]
((4, 22, 24), (46,))
Upvotes: 1