python实现MATLAB单指标索引

python实现MATLAB单指标索引,即用数字索引多维数组,MATLAB里A(4), 代表列向量A(:)(4),下面代码实现这种风格索引,当然是从0开始计数,但用过方法py2matlab, 实现从1开始计数。可从Pip下载:

 pip install pindex
# -*- coding: utf-8 -*-
'''pindex.py

a general method to 

example
    ind = SingleIndex([1,2,3])
    s = ind('helloworld')
    print(s)
    ind.py2matlab()
    s = ind('helloworld')
    print(s)

    ind = Index([1,2])
    ss = ind('helloworld')
    print(ss)

    ind = Index(([1,2],[3,4]))
    try:
        ss = ind(['hello', 'world'])
        print(ss)
    except:
        ind.py2matlab()
        ss = ind(['hello', 'world'])
        print(ss)

'''

import numpy as np


class Arith:
    # interface
    def __init__(self,*args):
        pass

    # override + -
    def __add__(self,other):
        pass

    def __radd__(self,other):
        return self+other

    def __neg__(self):
        pass

    def __pos__(self):
        return self

    def __iadd__(self,other):
        return self+other

    def __sub__(self,other):
        return self+(-other)

    def __isub__(self,other):
        return self-other

    def __rsub__(self,other):
        return -self+other

    #override * /
    def __mul__(self,other):
        pass

    def __rmul__(self,other):
        return self*other

    def __imul__(self,other):
        return self*other

    def __truediv__(self,other):
        pass

    def __itruediv__(self,other):
        return self/other

    def __rtruediv__(self,other):
        pass

    def __irtruediv__(self,other):
        return other/other

    def __pow__(self,other):
        pass

    def __ipow__(self,other):
        return self**other


def ind2tuple(ind, ns):
    '''single index (int) to multi-index under ns shape of array
    example:
>>> ind.ind2tuple(7,(3,4))
(1, 2)
>>> ind.ind2tuple(17,(3,4,3))
(2, 1, 1)'''
    dim = len(ns)
    if dim == 0:
        return ()
    if dim == 1:
        return (ind,)
    else:
        d, r = divmod(ind+1, np.prod(ns[:-1]))
        if r == 0:
            return tuple(ind-1 for ind in ns[:-1]) + (d-1,)
        else:
            return ind2tuple(r-1, ns[:-1]) + (d,)


def ind2iter(ind, N=None):
    # slice to range, int and list to list
    if isinstance(ind, list):
        return ind
    elif isinstance(ind, slice): # slc is a slice
        a = 0 if ind.start is None else ind.start
        b = N if ind.stop is None else ind.stop # b <= N
        return range(a, b, c) if ind.step else range(a, b)
    else:
        return [ind]


def addind(ind1, ind2):
    # ind1, ind2 are int list or range (iter)
    if isinstance(ind2, int):
        if isinstance(ind1, int):
            return ind1 + ind2
        elif isinstance(ind1, slice):
            return slice(ind1.start + ind2, ind1.stop + ind2, ind1.step)
        else:
            return [a+ind2 for a in ind1]
    elif isinstance(ind2, slice):
        if isinstance(ind1, int):
            return addind(ind2, ind1)
        elif isinstance(ind1, slice):
            return slice(ind1.start+ind2.start, ind1.stop+ind2.stop, (ind1.step if ind1.step!=None else 1)+(ind12.step if ind2.step!=None else 1))
        else:
            return list(map(np.add, ind1, ind2iter(ind2)))
    else:
        if isinstance(ind1, list):
            return list(map(np.add, ind1, ind2))
        else:
            return addind(ind2, ind1)


def mulind(ind, num):
    # ind is int list or slice, num is int
    if num==0:
        return 0
    if isinstance(num,int):
        if isinstance(ind, int):
            return ind1*num
        elif isinstance(ind, list):
            if num>0:
                return [num*a for a in ind]
            elif num<0:
                return [num*a for a in ind[::-1]]
        else:
            if num>0:
                return slice(num*ind.start, num*ind.stop, num*ind.step if ind.step!=None else num)
            elif num<0:
                return slice(num*ind.stop, num*ind.start, num*ind.step if ind.step!=None else num)
    elif isinstance(ind, int):
        return ind*num


class SingleIndex(Arith):
    '''single index: int, list, slice'''
    def __init__(self, value, N=None):
        # value: int, list, slice
        self.value = value

    def isa(self, T):
        return isinstance(self.value, T)

    def __add__(self, other):
        return SingleIndex(addind(self.value, other.value if isinstance(other, SingleIndex) else other))

    def __mul__(self, num):
        return SingleIndex(mulind(self.value, num))

    def __neg__(self):
        return -1 * self

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

    def __repr__(self):
        return 'Single Index: {0}'.format(self.value)

    def isempty(self, N=None):
        if self.isa(int):
            return False
        elif self.isa(list):
            return self.value == []
        else:
            a, b, c = self.value.start, self.value.stop, self.value.step
            if c is None or c > 0:
                if a is None or a == 0:
                    return b is None or -N < b < 0 or b > 0
                elif a > 0:
                    return b is None or b > a or b < 0 and b + N > a
                else:
                    return b is None or b > a + N or b < 0 and b > a
            else:
                if a is None or a == N - 1:
                    return b is None or -N <= b < -1 or b < N - 1
                elif a > 0:
                    return b is None or b < a or b < 0 and b + N < a
                else:
                    return b is None or b < a + N or b < 0 and b < a

    def max(self, N=None):
        if self.isa(int):
            return self.value
        elif self.isa(list):
            return max(self.value)
        else:
            a, b, c = self.value.start, self.value.stop, self.value.step
            if c is None or c > 1:
                b = N - 1 if b is None else b - 1
                if c == 1:
                    return b
                else:
                    r = mod(b - a, c)
                    return b - r
            else:
                return N - 1 if a is None else a


    def min(self, N=None):
        if self.isa(int):
            return self.value
        elif self.isa(list):
            return min(self.value)
        else:
            a, b, c = self.value.start, self.value.stop, self.value.step
            if c is None or c > 1:
                return 0 if a is None else a
            else:
                b = 0 if b is None else b + 1
                if c == -1:
                    return b
                else:
                    r = mod(a - b, -c)
                    return b - r


    def __call__(self, x):
        # self(x) == x[self]
        if isinstance(x, (str, tuple, list)):
            if self.isa((int, slice)):
                return x[self.value]
            else:
                return [x[k] for k in self.value]
        elif isinstance(x, np.ndarray):
            ns = x.shape
            if self.isa(int):
                return x[ind2tuple(self.value, ns)]
            elif self.isa(slice):
                return [x[ind2tuple(k, ns)] for k in ind2iter(self.value)]
            else:
                return [x[ind2tuple(k, ns)] for k in self.value]


    def py2matlab(self):
        # python style to matlab style
        if self.isa(int):
            self.value -= 1
        elif self.isa(slice):
            a, b, c = self.value.start, self.value.stop, self.value.step
            if c is None or c > 0:  # a <= b
                self.value = slice(a-1 if a else None, None if b == 0 else b, c)
            elif c < 0: # a >= b
                self.value = slice(a-1 if a else None, None if b == 1 or b is None else b-2, c)
        else:
            self.value = [v-1 for v in self.value]


    def matlab2py(self):
        # matlab style to python style; inverse of py2matlab
        if self.isa(int):
            self.value += 1
        elif self.isa(slice):
            a, b, c = self.value.start, self.value.stop, self.value.step
            if c is None or c > 0:
                if a is not None:
                    a += 1
            else:
                if a is not None:
                    a += 1
                if b is not None:
                    b += 2
            self.value = slice(a, b, c)
        else:
            self.value = [v+1 for v in self.value]



class Index(Arith, np.ndarray):
    # Multi-index, array of SingleIndex
    def __new__(cls, args=0):
        if isinstance(args, (int, list, slice)):
            return super(Index, cls).__new__(cls, shape=1, dtype=SingleIndex, buffer=np.array(SingleIndex(args)))
        elif isinstance(args, tuple):
            return super(Index, cls).__new__(cls, shape=len(args), dtype=SingleIndex, buffer=np.array([SingleIndex(a) for a in args]))
        else:
            raise TypeError('args should be of int, slice, list(of ints), tuple')


    @property
    def dim(self):
        return len(self)

    def __call__(self, array):
        # self(x) == x[self]
        if len(self) == 1:
            return self[0](array)
        else:
            return np.array([self[1:](a) for a in self[0](array)])

    def __add__(self, other):
        if isinstance(other, int):
            return Index(tuple(a+other for a in self))
        else:
            return Index(map(np.add, self, other))

    def __neg__(self):
        return Index(map(lambda x:-x, self))

    def __mul__(self,other):
        if np.isscalar(other):
            return Index(map(lambda x:x*other, self))
        else:
            return Index(map(np.add, self, other))

    def __str__(self):
        return ','.join(map(str, self))

    def minInd(self):
        # minimal index
        m = tuple(a.min() for a in self)
        return Index(m)

    def maxInd(self):
        # minimal index
        m = tuple(a.max() for a in self)
        return Index(m)

    def min_(self, *others):
        return Index(tuple(min(min(other.minInd()[k] for other in others), self.minInd()[k]) for k in range(self.dim)))

    def max_(self, *others):
        return Index(tuple(max(max(other.maxInd()[k] for other in others), self.maxInd()[k]) for k in range(self.dim)))

    def py2matlab(self):
        for ind in self:
            ind.py2matlab()

    def matlab2py(self):
        for ind in self:
            ind.matlab2py()


def irange(minInd, maxInd):
    return Index(tuple(slice(a, b+1) for a, b in zip(minInd, maxInd)))


if __name__ == '__main__':
    ind = Index(([0,1],[0,1]))
    A = [[1,2,6],[3,4,5]]
    B = ind(A)
    print(B)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值