Найти индекс, в котором преобразование типов завершается неудачно в массиве numpy - PullRequest
0 голосов
/ 26 апреля 2018

У меня есть массив массивов 1D, который мне нужно преобразовать в новый тип dtype. Новый тип может быть типом int, float или datetime . Некоторые строки могут быть недопустимыми для этого типа и не могут быть преобразованы, что приводит к ошибке, например:

>>> np.array(['10', '20', 'a'], dtype=int)
...
ValueError: invalid literal for int() with base 10: 'a'

Я хочу найти индекс этого недопустимого значения, в данном случае 2. В настоящее время я могу думать только о двух решениях, ни одно из которых не является хорошим:

  1. Выполните синтаксический анализ сообщения об исключении с помощью регулярного выражения, чтобы найти недопустимое значение, а затем найдите индекс этого значения в исходном массиве. Это кажется грязным и подверженным ошибкам.
  2. Разбор значений в цикле в Python. Это, вероятно, будет значительно медленнее, чем просто версия. Например, вот эксперимент, который я провел:
from timeit import timeit
import numpy as np

strings = np.array(list(map(str, range(10000000))))


def python_parse(arr):
    result = []
    for i, x in enumerate(arr):
        try:
            result.append(int(x))
        except ValueError:
            raise Exception(f'Failed at: {i}')


print(timeit(lambda: np.array(strings, dtype=int), number=10))  # 35 seconds
print(timeit(lambda: python_parse(strings), number=10))         # 52 seconds

Это выглядит как простая и достаточно распространенная операция, и я ожидаю, что решение будет встроено в библиотеку numpy, но я не могу ее найти.

Ответы [ 3 ]

0 голосов
/ 26 апреля 2018

Я бы сделал что-то вроде:

custom_type=int
i = 0
l = ['10', '20', 'a']
acc = np.array([], dtype=custom_type)
for elem in l:
    try:
       acc = np.concatenate((acc, np.array([elem], dtype=custom_type)))
       i += 1
    except:
       print("Failed to convert the type of the element in position {}".format(i))
0 голосов
/ 26 апреля 2018

Оказывается, я переоценил разницу между Python и numpy, и хотя код Python, который я поставил в вопросе, довольно медленный, его можно сделать намного быстрее, используя предварительно выделенный массив:

def python_parse(arr):
    result = np.empty(shape=(len(arr),), dtype=int)
    for i, x in enumerate(arr):
        try:
            result[i] = x
        except ValueError:
            raise Exception(f'Failed at: {i}')
    return result

Это приводит к ошибкам правильно и почти так же быстро, как просто np.array(strings, dtype=int) (что меня серьезно удивило).

0 голосов
/ 26 апреля 2018

Вы можете использовать np.core.defchararray.isdigit(), чтобы найти индексы цифр, а затем использовать логический операнд, чтобы получить индексы элементов с наноразрядными числами. После этого вы можете просто использовать np.where(), чтобы получить соответствующие индексы:

In [20]: arr = np.array(['10', '20', 'a', '4', '%'])

In [24]: np.where(~np.core.defchararray.isdigit(arr))
Out[24]: (array([2, 4]),)

Если вы хотите проверить несколько типов, таких как float, вы можете использовать пользовательскую функцию, а затем с помощью np.vectorize применить функцию к вашему массиву. Для дат это немного сложно, но если вам нужен общий способ для этого, вы можете использовать dateutils.parser().

Вы можете использовать функцию, подобную следующей:

# from dateutils import parser
In [33]: def check_type(item):
    ...:     try:
    ...:         float(item)
    ...:     except:
    ...:         try:         
    ...:             parser.parse(item)
    ...:         except:     
    ...:             return True
    ...:         else:      
    ...:             return False
    ...:     else:          
    ...:         return False

Тогда:

vector_func = np.vectorize(check_type)
np.where(vector_func(arr))

Демо-версия:

In [45]: arr = np.array(['10.34', '-20', 'a', '4', '%', '2018-5-01'])

In [46]: vector_func = np.vectorize(check_type)
    ...: np.where(vector_func(arr))
    ...: 
Out[46]: (array([2, 4]),)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...