Матрица преобразования чисел из float в строки

18

У меня есть массив поплавков, который я нормализовал к одному (т. е. наибольшее число в массиве равно 1), и я хотел использовать его в качестве цветовых индексов для графика. В использовании matplotlib для использования оттенков серого для этого требуется использовать строки от 0 до 1, поэтому я хотел преобразовать массив float в массив строк. Я пытаюсь сделать это, используя «astype (« str »)», но это создает некоторые значения, которые не совпадают (или даже близки) к оригиналам.

Я замечаю это, потому что matplotlib жалуется на поиск числа 8 в массиве, что является нечетным, поскольку оно было нормализовано для одного!

Короче говоря, у меня есть массив phis, float64, такой, что:

numpy.where(phis.astype('str').astype('float64') != phis)

не пусто. Это озадачивает, как (надеюсь, наивно), похоже, это ошибка в numpy, есть ли что-нибудь, что я мог бы сделать неправильно, чтобы вызвать это?

Изменить: после исследования это, по-видимому, связано с тем, как функция string обрабатывает высокоточные поплавки. Использование векторизованной функции toString (как из ответа грабителей), это также имеет место, однако, если лямбда-функция:

lambda x: "%.2f" % x

Тогда графическое произведение работает - любопытно и любопытно. (Очевидно, что массивы уже не равны!)

    
задан VolatileStorm 20.03.2011 в 00:05
источник

4 ответа

29

Похоже, вы немного смущены тем, как массивы numpy работают за кулисами. Каждый элемент в массиве должен быть того же размера .

Строковое представление поплавка не работает таким образом. Например, repr(1.3) дает '1.3' , но repr(1.33) дает '1.3300000000000001' .

Точное строковое представление числа с плавающей запятой создает строку variable length .

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

Если вы используете x.astype('str') , он всегда будет преобразовывать вещи в массив строк длиной 1.

Например, используя x = np.array(1.344566) , x.astype('str') дает '1' !

Вам нужно быть более понятным и использовать синтаксис '|Sx' dtype, где x - длина строки для каждого элемента массива.

Например, используйте x.astype('|S10') для преобразования массива в строки длиной 10.

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

    
ответ дан Joe Kington 21.03.2011 в 16:34
  • Обоснование использования массивов numpy строк состояло в том, что matplotlib требует правильной формы итерации строк, которые представляют числа от 0 до 1, чтобы представлять оттенки серого (которые в то время я хотел). Казалось, проще всего преобразовать массив чисел, который у меня был в массив строк. Я не ожидал усложнения длины. –  VolatileStorm 23.03.2011 в 11:13
  • полезно также в этой ситуации: 1.) читать данные из файла 2.) Предположим, что все записи являются float, однако некоторые из них являются nan. 3.) если все считываются как float, в списке будут переменные double64, которые отображаются как nan, но не распознаются как numpy.nan 4.), чтобы заменить их, я успешно использовал: если V [-1 ] .astype ('| S3') == 'nan': V [-1] = numpy.nan –  Schorsch 21.03.2014 в 16:25
  • вы можете использовать np.genfromtxt и иметь дело с этим (более или менее) автоматически. Это всегда плохая идея конвертировать float в строки, если вы собираетесь использовать их как float. –  Vincenzooo 16.05.2016 в 19:10
  • Я знаю, что это ~ 7 лет, но я комментирую, потому что это уже не так (python 3.6; np 1.14.0) –  Mohammad Athar 13.02.2018 в 18:17
12

Если у вас есть массив numbers и вам нужен массив strings , вы можете написать:

strings = ["%.2f" % number for number in numbers]

Если ваши числа являются поплавками, массив будет массивом с теми же числами, что и строки с двумя десятичными знаками.

>>> a = [1,2,3,4,5]
>>> min_a, max_a = min(a), max(a)
>>> a_normalized = [float(x-min_a)/(max_a-min_a) for x in a]
>>> a_normalized
[0.0, 0.25, 0.5, 0.75, 1.0]
>>> a_strings = ["%.2f" % x for x in a_normalized]
>>> a_strings
['0.00', '0.25', '0.50', '0.75', '1.00']

Обратите внимание, что он также работает с numpy массивами:

>>> a = numpy.array([0.0, 0.25, 0.75, 1.0])
>>> print ["%.2f" % x for x in a]
['0.00', '0.25', '0.50', '0.75', '1.00']

Аналогичную методологию можно использовать, если у вас многомерный массив:

new_array = numpy.array(["%.2f" % x for x in old_array.reshape(old_array.size)])
new_array = new_array.reshape(old_array.shape)

Пример:

>>> x = numpy.array([[0,0.1,0.2],[0.3,0.4,0.5],[0.6, 0.7, 0.8]])
>>> y = numpy.array(["%.2f" % w for w in x.reshape(x.size)])
>>> y = y.reshape(x.shape)
>>> print y
[['0.00' '0.10' '0.20']
 ['0.30' '0.40' '0.50']
 ['0.60' '0.70' '0.80']]

Если вы проверите пример Matplotlib для используемой функции , вы заметите, что они используйте аналогичную методологию: постройте пустую матрицу и заполните ее строками, построенными методом интерполяции. Соответствующая часть ссылочного кода:

colortuple = ('y', 'b')
colors = np.empty(X.shape, dtype=str)
for y in range(ylen):
    for x in range(xlen):
        colors[x, y] = colortuple[(x + y) % len(colortuple)]

surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
        linewidth=0, antialiased=False)
    
ответ дан Escualo 20.03.2011 в 00:14
  • Это был не вопрос. Вы создаете список, ему нужен массив numpy. –  Chinmay Kanchi 20.03.2011 в 00:18
  • Приносим извинения, если это было непонятно, но я имею дело с массивами numpy, а не с списками python. Более того, мой массив является 2-мерным, поэтому понимание списка 1dim не будет работать. Я полностью понимаю, что я могу создать промежуточный список python, а затем преобразовать в массив numpy, но похоже, что этот метод выше должен работать и что это дополнительное (медленное) программирование для использования промежуточного списка. –  VolatileStorm 20.03.2011 в 00:19
  • Если объект можно повторить (например, список или массив numpy), он поддерживает понимание списка. Это не обязательно должен быть список (утиная печать) –  Escualo 20.03.2011 в 00:25
  • Да, но вы не получаете массив numpy, не так ли? –  Chinmay Kanchi 20.03.2011 в 00:31
  • Arrieta: это не сработает, потому что понимание списка будет итерировать поверх numpy.ndarrays, а не одиночных чисел, когда используется многомерный массив –  robbles 20.03.2011 в 00:36
Показать остальные комментарии
1

Это, вероятно, медленнее, чем вы хотите, но вы можете сделать:

>>> tostring = vectorize(lambda x: str(x))
>>> numpy.where(tostring(phis).astype('float64') != phis)
(array([], dtype=int64),)

Похоже, он округляет значения, когда он преобразуется в str из float64, но таким образом вы можете настроить преобразование, как вам нравится.

    
ответ дан robbles 20.03.2011 в 00:29
  • Это тоже не работает, что заставляет меня предположить, что преобразование очень маленьких чисел в строки, не удается? То есть массив содержит числа порядка 10 ^ -30. –  VolatileStorm 20.03.2011 в 00:36
  • вы имеете в виду, что получаете другой результат? Я попробовал это сейчас с помощью небольшого 2D-массива, и он сработал - может быть, это ошибка ... –  robbles 20.03.2011 в 00:38
  • Хорошо, теперь я вижу то же самое с очень маленькими числами. Может быть, это общая математическая проблема с плавающей запятой? –  robbles 20.03.2011 в 00:44
  • Я получаю другой результат, но, возможно, ограничение не связано с порядком величины числа, а с точностью до точности (в то время как описано в научной нотации). Изменить. Если проблема с плавающей точкой, то какая ошибка с плавающей запятой допускает число, намного меньшее, чем 1, как около 8? ха-ха –  VolatileStorm 20.03.2011 в 00:44
1

Если основной проблемой является потеря точности при преобразовании из float в строку, одним из возможных способов перехода является преобразование float в decimal S: Ссылка .

В python 2.7 и выше вы можете напрямую преобразовать float в объект decimal .

    
ответ дан ev-br 20.03.2011 в 11:07