Векторизованный эквивалент dict.get - PullRequest
0 голосов
/ 07 марта 2020

Я ищу функциональность, которая работает следующим образом

lookup_dict = {5:1.0, 12:2.0, 39:2.0...}
# this is the missing magic:
lookup = vectorized_dict(lookup_dict)

x = numpy.array([5.0, 59.39, 39.49...])

xbins = numpy.trunc(x).astype(numpy.int_)  
y = lookup.get(xbins, 0.0)

# the idea is that we get this as the postcondition:    
for (result, input) in zip(y, xbins):
     assert(result==lookup_dict.get(input, 0.0))

Есть ли в numpy (или scipy) какая-то разновидность разреженного массива, которая получает такую ​​функциональность?

Полный контекст заключается в том, что я собираю некоторые образцы 1-D функции.

Ответы [ 3 ]

1 голос
/ 07 марта 2020

Насколько я знаю, numpy не поддерживает разные типы данных в одних и тех же структурах массива, но вы можете достичь аналогичного результата, если вы хотите отделить ключи от значений и поддерживать ключи (и соответствующие значения) в отсортированном виде. order:

import numpy as np

keys   = np.array([5,12,39])
values = np.array([1.0, 2.0, 2.0])

valueOf5 = values[keys.searchsorted(5)] # 2.0


k = np.array([5,5,12,39,12]) 

values[keys.searchsorted(k)] # array([1., 1., 2., 2., 2.])

Это может быть не так эффективно, как ключ хеширования, но он поддерживает распространение косвенных значений из массивов с любым числом измерений.

обратите внимание, что это предполагает Ваши ключи всегда присутствуют в массиве ключей. Если нет, то вместо ошибки вы можете получить значение от следующего ключа вверх.

1 голос
/ 07 марта 2020

Использование np.select для создания логических масок для массива ([xbins == k for k in lookup_dict]), значений из dict (lookup_dict.values()) и значения по умолчанию 0:

y = np.select(
    [xbins == k for k in lookup_dict], 
    lookup_dict.values(), 
    0.0
)
# In [17]: y
# Out[17]: array([1., 0., 2.])

Предполагается, что словарь отсортирован, я не уверен, что поведение будет ниже python 3.6.

ИЛИ перебор с pandas:

import pandas as pd
s = pd.Series(xbins)
s = s.map(lookup_dict).fillna(0)
0 голосов
/ 07 марта 2020

Другой подход заключается в использовании сортировки поиска для поиска в массиве numpy, который имеет целое число «ключи» и возвращает изначально загруженное значение в диапазоне n <= x < n+1. Это может быть полезно для тех, кто задает аналогичный вопрос в будущем.

import numpy as np

class NpIntDict:
    """ Class to simulate a python dict get for a numpy array.  """
    def __init__( self, dict_in, default = np.nan ):
        """  dict_in: a dictionary with integer keys.
             default: the value to be returned for keys not in the dictionary.
                      defaults to np.nan
             default must be consistent with the dtype of values
        """
        # Create list of dict items sorted by key.
        list_in = sorted([ item for item in dict_in.items() ])
        # Create three empty lists.
        key_list = []   
        val_list = [] 
        is_def_mask = []
        for key, value in list_in:
            key = int(key)
            if not key in key_list:   # key not yet in key list
                # Update the three lists for key as default.
                key_list.append( key )      
                val_list.append( default )
                is_def_mask.append( True )
            # Update the lists for key+1.  With searchsorted this gives the required results.
            key_list.append( key + 1 )
            val_list.append( value )
            is_def_mask.append( False )
        # Add the key > max(key) to the val and is_def_mask lists.
        val_list.append( default )
        is_def_mask.append( True )
        self.keys = np.array( key_list, dtype = np.int )
        self.values = np.array( val_list )
        self.default_mask = np.array( is_def_mask )

    def set_default( self, default = 0 ):
        """  Set the default to a new default value.  Using self.default_mask.
             Changes the default value for all future self.get(arr).
        """
        self.values[ self.default_mask ] = default

    def get( self, arr, default = None ):
        """  Returns an array looking up the values in `arr` in the dict.
             default can be used to change the default value returned for this get only.
        """
        if default is None:
            values = self.values
        else:
            values= self.values.copy()
            values[ self.default_mask ] = default
        return values[ np.searchsorted( self.keys, arr, side = 'right' ) ]
        # side = 'right' to ensure key[ix] <= x < key[ix+1]
        # side = 'left' would mean key[ix] < x <= key[ix+1]

Это может быть упрощено, если не требуется изменять значение по умолчанию, возвращаемое после создания NpIntDict.

Кому протестируйте его.

d = { 2: 5.1, 3: 10.2, 5: 47.1, 8: -6}

# x <2 Return default
# 2 <= x <3 return 5.1
# 3 <= x < 4 return 10.2
# 4 <= x < 5 return default
# 5 <= x < 6 return 47.1
# 6 <= x < 8 return default
# 8 <= x < 9 return -6.
# 9 <= x return default

test = NpIntDict( d, default = 0.0 )
arr = np.arange( 0., 100. ).reshape(10,10)/10
print( arr )
"""
[[0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
 [1.  1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9]
 [2.  2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9]
 [3.  3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9]
 [4.  4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9]
 [5.  5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9]
 [6.  6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9]
 [7.  7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9]
 [8.  8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9]
 [9.  9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9]]
"""

print( test.get( arr ) )
"""
[[ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]
 [ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]
 [ 5.1  5.1  5.1  5.1  5.1  5.1  5.1  5.1  5.1  5.1]
 [10.2 10.2 10.2 10.2 10.2 10.2 10.2 10.2 10.2 10.2]
 [ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]
 [47.1 47.1 47.1 47.1 47.1 47.1 47.1 47.1 47.1 47.1]
 [ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]
 [ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]
 [-6.  -6.  -6.  -6.  -6.  -6.  -6.  -6.  -6.  -6. ]
 [ 0.   0.   0.   0.   0.   0.   0.   0.   0.   0. ]]
"""

Это можно изменить, чтобы вызвать исключение, если какой-либо из элементов arr отсутствует в списке ключей. Для меня возвращение по умолчанию было бы более полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...