0

I was surprised this question has not been asked yet. Given an array of integers

x = np.random.randint(1,4,10)

> array([3, 2, 1, 2, 1, 1, 3, 3, 2, 1])

How can I (easily) change all values according to some systematic rule, such as

x[x==1] = 5
x[x==2] = 6
x[x==3] = 7

> array([6, 5, 6, 7, 5, 5, 6, 5, 7, 6])

The rule may be more complex than this, e.g. recode 1=2 and 2=1 so that the order plays a role.

tomka
  • 2,516
  • 7
  • 31
  • 45
  • 1
    This is a great application for using a dictionary as a direct look-up list. Or, if the transfer function is this simple, then you simply apply the rule of `item += 4` on the entire list. – Prune May 17 '17 at 19:40

3 Answers3

2

The most straightforward way would just be to iterate over the array replacing values.

rule = {1: 5, 2: 6, 3: 7}
x = x.vectorize(lambda x: rule[x] if x in rule else x)
ephemient
  • 198,619
  • 38
  • 280
  • 391
2

Use map to do the transformation. map takes a transformation function and a list of inputs and outputs the result of applying that function to all of the inputs systematically.

For example:

def mapping(i):
    if i == 1:
        return 5
    elif i == 2:
        return 6
    elif i == 3:
        return 7
    return i

print array(map(mapping, [3, 2, 1, 2, 1, 1, 3, 3, 2, 1]))
Mark Beilfuss
  • 602
  • 4
  • 12
1

While others may suggest just using a dictionary, I would suggest using something that is actually made for this particular type of behavior rather manually using a dictionary, Python's map function.

map takes a function and applies it to each input, and returns the result of that as a list. So you would create your if x == y, return z as a function and apply map(yourfunction, yourlist) to it.

The nice thing about map is that you can use it on any iterable, and not just your list

Normal python version

def numberfunc(x):
    if x == 1:
       return 5

    elif x == 2:
       return 6

    elif x== 3:
       return 7

    else:
        return x

x = [random.randint(1,4) for _ in range(10)]
# array([3, 2, 1, 2, 1, 1, 3, 3, 2, 1])

x = map(numberfunc, x)
# array([6, 5, 6, 7, 5, 5, 6, 5, 7, 6])

Numpy version (numpy uses vectorize as its map)

x = np.random.randint(1,4,10)
# array([3, 2, 1, 2, 1, 1, 3, 3, 2, 1])

x = x.vectorize(numberfunc)
# array([6, 5, 6, 7, 5, 5, 6, 5, 7, 6])

Additionally if your number map function had a large amount of comparisons (larger than the 3 you list here) you might consider using python's dict in order to increase performance given O(1) armortized element access times, for example:

num_dict {1:5, 2:6, 3:7, ...}
def numberfunc(x):
        if x in num_dict:
            return num_dict[x]
        return x

You probably wouldn't want to do this in the simple case because of the armortized gotcha on dictionary run time performance and constant overhead. Amortized basically means that taking into account all operations you will make that are armortized, the cost of the most expensive operations will be offset by the cheapness of the less expensive ones. Occasionally dictionary mutation can be expensive; taken as a whole, many dictionary mutations will not be expensive compared to other data structures. Dictionaries also have larger indexing overheads compared to things like arrays. In a naive implementation, you will typically have to iterate over lists given your key to find your value and in more complicated scenarios, often more guaranteed overhead is involved

Krupip
  • 4,404
  • 2
  • 32
  • 54