Home > Software engineering >  change python list (mutable object in general) values with something like map, but not returning new
change python list (mutable object in general) values with something like map, but not returning new

Time:09-30

Python map function works both on mutables and immutables, it returns new iterable object(for python 3.x).

However I would like only to change the values (of a mutable object eg. list, np.ndarray), in the nice and consistent manner similar to the map().

Given following function:

def perturbation_bin(x, prob):
    '''
    Perturbation operation for binary representation. Decide independently for each bit, if it will be inverted or not.

    :param x: binary chromosome
    :param prob: bit inversion probability
    :return: None
    '''
    assert x.__class__ == np.ndarray, '\nBinary chromosome must np.ndarray.'
    assert x.dtype == np.int8, '\nBinary chromosome np.dtype must be np.int8.'
    x[np.where(np.random.rand(len(x)) <= prob)]  = 1
    map(partial(np.mod, x2=2), x)

This code for each 'bit' (np.int8) randomly adds or adds not 1, changing the numpy ndarray object passed to the function. However in the next step I would like to apply modulo to all elements of this np.ndarray. As the code is written now, it only creates new map object inside the function.

Does such map-like function in python exist, not returning new object but altering the mutable object values instead?

Thank you

CodePudding user response:

No, it does not. But you could easily make one, as long as the object you are willing to modify is a Sequence (specifically, you would need __len__, __getitem__ and __setitem__ to be defined):

def apply(obj, func):
    n = len(obj)
    for i in range(n):
        obj[i] = func(obj[i])
    return obj  # or None

This is not going to be the fastest execution around, but it may be accelerated with Numba (provided that you work with suitable objects, e.g. Numpy arrays).


As for your specific problem you can replace:

map(partial(np.mod, x2=2), x)

with:

apply(x, lambda x: x % 2)

or, with a much more performant:

import numba as nb


@nb.njit
def apply_nb(obj, func):
    n = len(obj)
    for i in range(n):
        obj[i] = func(obj[i])
    return obj


@nb.njit
def mod2_nb(x):
    return x % 2


apply_nb(x, mod2_nb)

Note that many NumPy functions, like np.mod() also support a out parameter:

np.mod(x, 2, out=x)

which will perform the operation in-place.


Timewise, the Numba-based operation seems to have an edge:

import numpy as np


x = np.random.randint(0, 1000, 1000)


%timeit X = x.copy(); np.mod(X, 2, out=X)                                                                       
# 9.47 µs ± 22.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit X = x.copy(); apply_nb(X, mod2_nb)                                                                      
# 7.51 µs ± 173 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit X = x.copy(); apply(X, lambda x: x % 2)                                                                 
# 350 µs ± 4.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  • Related