2

I am wondering how 2D array slicing can be implemented in Python?

For example,

arr is an instance of a self-defined class 2D array.

if I want to enable 2D slicing syntax on this object like this:

arr[:,1:3] #retrieve the 1 and 2 column values of every row

or

arr[,:3] #retrieve the 1 and 2 column values of every row

The usage and syntax is just like numpy.array. But how can this kind of functionality be realized on our own?

PS:

What I have in mind:

for the first case, the [:,1:3] part is like a tuple of two slices

However, for the second case [,1:3] appears to be quite mysterious.

xiaohan2012
  • 9,870
  • 23
  • 67
  • 101

4 Answers4

12

If you want to know the rules of array slicing, the picture below might help:

enter image description here

wilbeibi
  • 3,403
  • 4
  • 25
  • 44
4

For read access you need to override the __getitem__ method:

class ArrayLike(object):
    def __init__(self):
        pass
    def __getitem__(self, arg):
        (rows,cols) = arg # unpack, assumes that we always pass in 2-arguments
        # TODO: parse/interpret the rows/cols parameters,
        # for single indices, they will be integers, for slices, they'll be slice objects
        # here's a dummy implementation as a placeholder 
        return numpy.eye(10)[rows, cols]

One of the tricky bits is that __getitem__ always only uses one argument (aside from self), when you put multiple-comma-separated items inside the square brackets, you're actually providing a single tuple as the argument to the __getitem__ call; thus the need to unpack this tuple (and optionally verify that the length of the tuple is suitable) inside the function.

Now given a = ArrayLike(), you end up with

  • a[2,3] means rows=2, cols=3
  • a[:3,2] means rows=slice(None, 3, None), cols=3

and so on; you'll have to look at the documentation on slice objects to decide how you want to use the slice information to pull out the data that you need from your class.

To make it more like a numpy array, you'd also want to override __setitem__ as well, to allow for assigning to elements/slices.

Dave
  • 7,555
  • 8
  • 46
  • 88
1

obj[,:3] is not valid python so it will raise a SyntaxError -- Therefore, you can't have that syntax in your source file. (It fails when you try to use it on a numpy array as well)

mgilson
  • 300,191
  • 65
  • 633
  • 696
0

Here is a hack if it's your own class and you are willing to pass in a string.

How to override the [] operator?

class Array(object):

    def __init__(self, m, n):
        """Create junk demo array."""
        self.m = m
        self.n = n
        row = list(range(self.n))
        self.array = map(lambda x:row, range(self.m))

    def __getitem__(self, index_string):
        """Implement slicing/indexing."""

        row_index, _, col_index = index_string.partition(",")

        if row_index == '' or row_index==":":
            row_start = 0
            row_stop = self.m
        elif ':' in row_index:
            row_start, _, row_stop = row_index.partition(":")
            try:
                row_start = int(row_start)
                row_stop = int(row_stop)
            except ValueError:
                print "Bad Data"
        else:
            try:
                row_start = int(row_index)
                row_stop = int(row_index) + 1
            except ValueError:
                print "Bad Data"

        if col_index == '' or col_index == ":":
            col_start = 0
            col_stop = self.n
        elif ':' in col_index:
            col_start, _, col_stop = col_index.partition(":")
            try:
                col_start = int(col_start)
                col_stop = int(col_stop)
            except ValueError:
                print "Bad Data"
        else:
            try:
                col_start = int(col_index)
                col_stop = int(col_index) + 1
            except ValueError:
                print "Bad Data"

        return map(lambda x: self.array[x][col_start:col_stop],
                       range(row_start, row_stop))

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

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


array = Array(4, 5)
print array
out: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]

array[",1:3"]
out: [[1, 2], [1, 2], [1, 2], [1, 2]]

array[":,1:3"]
out: [[1, 2], [1, 2], [1, 2], [1, 2]]
Community
  • 1
  • 1
poof
  • 69
  • 5