Матрица преобразования чисел из float в строки
У меня есть массив поплавков, который я нормализовал к одному (т.е. наибольшее число в массиве равно 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
Тогда графическое произведение работает - любопытно и любопытно. (Очевидно, что массивы уже не равны!)
Ответы
Ответ 1
Похоже, вы немного смущены тем, как массивы numpy работают за кулисами. Каждый элемент массива должен быть одного размера.
Строковое представление поплавка не работает таким образом. Например, repr(1.3)
дает '1.3'
, но repr(1.33)
дает '1.3300000000000001'
.
Точное строковое представление числа с плавающей точкой создает строку переменной длины.
Поскольку массивы numpy состоят из элементов с одинаковым размером, numpy требует указать длину строк в массиве при использовании строковых массивов.
Если вы используете x.astype('str')
, он всегда будет преобразовывать объекты в массив строк длиной 1.
Например, используя x = np.array(1.344566)
, x.astype('str')
дает '1'
!
Вам нужно быть более эксплицитным и использовать синтаксис '|Sx'
dtype, где x
- длина строки для каждого элемента массива.
Например, используйте x.astype('|S10')
для преобразования массива в строки длиной 10.
Еще лучше, просто избегайте использования массивов numpy строк. Это, как правило, плохая идея, и нет причин, которые я могу видеть из вашего описания вашей проблемы, чтобы использовать их в первую очередь...
Ответ 2
Если у вас есть массив 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)
Ответ 3
Это, вероятно, медленнее, чем вы хотите, но вы можете сделать:
>>> tostring = vectorize(lambda x: str(x))
>>> numpy.where(tostring(phis).astype('float64') != phis)
(array([], dtype=int64),)
Похоже, он округляет значения, когда он преобразуется в str из float64, но таким образом вы можете настроить преобразование, как вам нравится.
Ответ 4
Если основной проблемой является потеря точности при преобразовании из float в строку, одним из возможных способов перехода является преобразование float в decimal
S: http://docs.python.org/library/decimal.html.
В python 2.7 и выше вы можете напрямую преобразовать float в объект decimal
.