Поиск столбец-индексы многомерного массива, которые соответствуют нескольким условиям (одно условие в строке)


Я написал сценарий, я верю, работает и охватывает все пограничные случаи. Мне любопытно о том, чтобы улучшить скорость. Хотя данный пример охватывает многомерный массив 3 rows x 10 columnsмое фактическое использование случае будет n rows x ~70,000 columns (где n зависит от количества данных параметров поиска).

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

Я включил небольшой class имени MaskOps() потому что он имеет много других функций, необходимых в моей основной код, хотя я только включил деталями соответствующие цели в этот вопрос.

import numpy as np

class MaskOps():

    @staticmethod
    def get_base(shape, value, dtype=int):
        """ This function produces a base-mask, the values of which may be overwritten. """
        if isinstance(value, (float, int)):
            res = np.ones(shape, dtype=dtype) * value
        elif isinstance(value, str):
            res = np.array([value for idx in range(np.prod(shape))]).reshape(shape)
        return res

    @staticmethod
    def alternate_base(shape, key):
        """ This function creates base-masks that consist of one of two value; the value depends on the index input as the parameter key. """
        if key % 2 == 0:
            value = 0.25
        else:
            value = 0.5
        return MaskOps().get_base(shape, value, dtype=float)

MO = MaskOps()

Образец Данных

row_a = np.linspace(1, 10, 10)
row_b = row_a * 10
row_c = row_a + 20
data = np.array([row_a, row_b, row_c])

Основная Функция Поиск

def core_algorithm(ndata, search_value):
    """ 
    This function prints values and indices that match the search condition. 

    An index mask of non-zero values is created per row of the input data, 
    and the values of the index mask are overwritten to be a zero at each
    column at which the condition is satisfied - per condition and row of data.
    Then, the columns of the index masks that sum to zero are the column-indices 
    that satisfy all input conditions.
    """

    print("\nSEARCH VALUES:\n{}\n".format(search_value))
    print("NDATA:\n{}\n".format(ndata))

    bases = np.array([MO.alternate_base(len(ndata.T), idx) for idx in range(len(ndata))])
    print("ORIGINAL BASES:\n{}\n".format(bases))

    locs = np.array([np.where(ndata[idx] == search_value[idx])[0] for idx in range(len(search_value))])
    print("LOCS:\n{}\n".format(locs))

    for idx in range(len(bases)):
        bases[idx][locs[idx]] = 0
    print("UPDATED BASES:\n{}\n".format(bases))

    res_idx = np.where(np.sum(bases, axis=0) == 0)[0]
    print("RES COLUMN:\n{}\n".format(res_idx))

    if len(res_idx) == 0:
        raise ValueError("match could not be found")

    res_val = np.array([ndata[idx][res_idx] for idx in range(len(ndata))])
    print("VALUES FROM COL-INDICES\n{}\n".format(res_val))

core_algorithm(data, search_value=(3, 30, 23)) # works successfully
# core_algorithm(data, search_value=(3, 30, 24)) # throws an error

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



678
1
задан 12 марта 2018 в 07:03 Источник Поделиться
Комментарии
1 ответ

Python-это не JAVA

Не все должно быть в class

class MaskOps():

@staticmethod
def get_base(shape, value, dtype=int):
""" This function produces a base-mask, the values of which may be overwritten. """
if isinstance(value, (float, int)):
res = np.ones(shape, dtype=dtype) * value
elif isinstance(value, str):
res = np.array([value for idx in range(np.prod(shape))]).reshape(shape)
return res

@staticmethod
def alternate_base(shape, key):
""" This function creates base-masks that consist of one of two value; the value depends on the index input as the parameter key. """
if key % 2 == 0:
value = 0.25
else:
value = 0.5
return MaskOps().get_base(shape, value, dtype=float)

MO = MaskOps()

может просто быть

def get_base(..):
...
def alternate_base(...):
...

зацикливание

Python имеет много элегантные циклические конструкции

for idx in range(len(bases)):
bases[idx][locs[idx]] = 0

например не один из них. Это может быть сделано с zip

for base, loc in zip(bases, locs):
base[loc] = 0

Проверьте эти разговоры (слайды) советы по enumerate, zip, generators и т. д.

возвращаемые значения, не печатать их

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

использовать numpy (#1)

У вас есть много массивов numpy, что вы делаете с списочные включения. Было бы намного проще и яснее, чтобы векторизовать этот, и использовать numpyС большим арсеналом собственных методов

def get_base(shape, value, dtype=int):
""" This function produces a base-mask, the values of which may be overwritten. """
if isinstance(value, (float, int)):
res = np.ones(shape, dtype=dtype) * value
elif isinstance(value, str):
res = np.array([value for idx in range(np.prod(shape))]).reshape(shape)
return res

можно легко записать как

def get_base(shape, value, dtype=int):
dtypes = {int: int, float: float}
if not dtype:
dtype = dtypes.get(type(value), object)
return np.ones(shape, dtype=dtype) * value

использовать NumPy и обратно (#2)

Вместо того, чтобы сделать этот логический маску себе, почему бы просто не сделать:

mask = data == [[i] for i in search_value]
collapsed_mask = mask.all(axis=0)
result = data[:, collapsed_mask]

1
ответ дан 12 марта 2018 в 04:03 Источник Поделиться