Разумный способ иметь разные версии None? - PullRequest
6 голосов
/ 14 марта 2019

Работа в Python3.

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

 [[.3, 1.2, 0.5],
  [.6, .7],
  [1.4, .9, .5, .7],
  [.2, .3, .1, .7, .1]]

Кроме того, вы решаете сохранить это в массиве Numpy, для которого вы дополняетесписки с None (numpy преобразует это в np.nan).

 [[.3, 1.2, 0.5, None, None],
  [.6, .7, None, None, None],
  [1.4, .9, .5, .7, None],
  [.2, .3, .1, .7, .1]]

Но есть проблема, значения, представленные как None, могут быть None по одной из 3 причин;

  1. Жук не имеет много пятен;этого количества не существует.

  2. Жук не будет стоять на месте, и вы не сможете измерить место.

  3. У вас нетЯ уже приступил к измерению этого жука, поэтому значение не назначено.

Моя проблема на самом деле не связана с жуками, но принципы те же.Мне нужны 3 разных значения None, чтобы я мог различать причины пропущенных значений.Мое текущее решение состоит в том, чтобы использовать значение, настолько большое, что это физически маловероятно, но это не очень безопасное решение.

Предположим, вы не можете использовать отрицательные числа - на самом деле измеряемая мной величина может бытьотрицательный.

Данные большие, важна скорость чтения.

Редактировать;комментарии справедливо указывают на то, что говорить о скорости важно, не говоря о том, какие операции немного бессмысленны.Принципиальный компонентный анализ, вероятно, будет использоваться для декорриляции переменной, вычисления евклидова квадрата расстояния для алгоритма кластеризации (но данных в этой переменной немного), возможно, для некоторой интерполяции.В конце концов, это рекурсивная нейронная сеть, но она будет исходить из библиотеки, поэтому мне просто нужно будет поместить данные в форму ввода.Так что, может быть, нет ничего хуже, чем линейная алгебра, все должно уместиться в оперативной памяти, если я осторожен, я думаю.

Что такое хорошая стратегия?

Ответы [ 5 ]

5 голосов
/ 14 марта 2019

Самый простой способ - использовать строки: «не учтено», «неизвестно» и «нет данных». Однако, если вы хотите быстро обрабатывать в numpy, массивы со смешанными числами / объектами не ваши друзья.

Я бы предложил добавить несколько массивов той же формы, что и ваши данные, состоящие из 0 и 1. Таким образом, массив missing = 1, где пятно отсутствует, еще 0 и т. Д., То же самое с массивом not_measured и т.д ..

Затем вы можете использовать NaN повсюду, а затем маскировать свои данные, скажем, np.where(missing == 1), чтобы легко найти нужные вам NaN.

3 голосов
/ 14 марта 2019

Вот решение (отказ от ответственности: HACK!), Которое позволяет избежать скачков скорости, таких как объект dtype или отдельные маски:

Похоже, что вокруг fp-представления nan:

довольно много "мертвого пространства"
>>> nan_as_int = np.array(np.nan).view(int)[()]
>>> nan_as_int
9221120237041090560

>>> custom_nan = np.arange(nan_as_int, nan_as_int+10).view(float)
>>> custom_nan
array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan])

Мы создали десять различных nan с. Обратите внимание, что это отличается от создания нескольких экземпляров с использованием float("nan"). Все такие экземпляры будут отображаться в одно и то же значение в numpy и, следовательно, будут неразборчивы после помещения в массив необъектов.

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

>>> def which_nan(a):
...     some_nan = np.isnan(a)
...     return np.where(some_nan, np.subtract(a.view(int), nan_as_int, where=some_nan), -1)

Пример:

>>> exmpl = np.array([0.1, 1.2, custom_nan[3], custom_nan[0]])
>>> exmpl
array([0.1, 1.2, nan, nan])
>>> which_nan(exmpl)
array([-1, -1,  3,  0], dtype=int64)

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

>>> which_nan(np.sin(exmpl))
array([-1, -1,  3,  0], dtype=int64)
3 голосов
/ 14 марта 2019

Если вы просто хотите, чтобы объект не имел какого-либо известного значения, а также не None, просто создайте новый объект:

NOT_APPLICABLE = object()
NOT_MEASURED = object()
UNKNOWN = object()

Теперь вы можете использовать эти значения точно так же, как и None:

[1.4, .9, .5, .7, UNKNOWN]

...

if value is UNKNOWN:
    # do something

и т.д.

Если вам нужно значение, которое может быть представлено в виде float (например, в массиве numpy), вы можете создать значение NaN с «дополнительными» данными, закодированными в мантиссе. Однако это может быть небезопасно , поскольку нет гарантии того, что эти биты сохраняются посредством различных операций над значениями.

2 голосов
/ 14 марта 2019

В комментарии ниже вопрос, который я задаю, почему бы не использовать np.inf, -np.inf и np.nan и ответ автора, что это то, что ему нужно.

Поэтому я добавляю пост, потому что люди чаще смотрят на отзывы, а не комментарии.

1 голос
/ 14 марта 2019

Было предложено создать три разных экземпляра object для каждого вашего случая.

Поскольку вы хотите, чтобы эти объекты имели свойства NaN, вы можете попробовать создать три разных экземпляра NaN.

NOT_APPLICABLE = float("nan")
NOT_MEASURED = float("nan")
UNKNOWN = float("nan")

Это предел возможности взлома, поэтому используйте на свой страх и риск, но я не верю, что какая-либо реализация Python оптимизирует NaN, чтобы всегда повторно использовать один и тот же объект.Тем не менее, вы можете добавить условие дозорного, чтобы проверить это перед запуском.

if NOT_APPLICABLE is NOT_MEASURED or NOT_MEASURED is UNKNOWN or UNKNOWN is NOT_APPLICABLE :
    raise ValueError # or try something else

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

row = [1.0, 2.4, UNKNOWN]

...

if value is UNKNOWN:
    ...

Между тем, он сохраняет любую оптимизацию numpy, которая может быть связана с его массивом.

Раскрытие: это хакерское предложение, я очень хочу услышать от других об этом.

...