Подтвердить что ты не робот

Преобразование знака минус-символа Юникода (из титлебелей matplotlib)

У меня проблема с объектом Text, который используется matplotlib для представления меток.

По причине тестирования мне нужно проверить значение меток тиков, созданных на графике. Если метка является строкой или положительным числом, нет никаких проблем: возвращается строка юникода, я проверяю ее (или конвертирую ее в число, учитывая обстоятельства), и все в порядке.

Но если метка является отрицательным числом, то я вернусь - это искаженная строка юникода по той причине, которую я не могу понять.

Возьмем этот пример кода:

import pylab as plt
fig, ax = plt.subplots(1)
ax.plot([-1, 0, 1, 2], range(4))
labels = ax.get_xticklabels()

теперь, если я задаю текстовое содержимое второй метки (0), я получаю нормальную строку юникода:

labels[1].get_text()
# u'0.0'

но юникод первого (-1) - странная вещь

labels[1].get_text()
# u'\u22121'

Это правильно напечатано в терминале, но в этом случае мне нужно столкнуться с ним с числовым значением, и каждое преобразование завершится неудачно, как с int, так и float.

Я попытался преобразовать его в строку UTF-8 с помощью

text = labels[1].get_text()
text.encode('utf8')
# '\xe2\x88\x921'

но опять-таки это что-то, что правильно напечатано и вызывает ошибку при преобразовании. Я также посмотрел на модуль unicodedata, но похоже, что он может конвертировать только один символ, поэтому в этом случае бесполезно. Я также попытался нормализовать строку с помощью unicodedata.normalize и любого возможного формата, но опять же не удался.

Я перешел в модуль pipy unidecode (как это предложено в Python и нормализация символов), без каких-либо успехов

from unidecode import unidecode
unidecode(text)
# '[?]1'

Я также пытался избежать проблем с шрифтами, используя решение в не-ASCII-символах в Matplotlib, но с тем же результатом (я не уверен, даже иметь что-то делать, будучи проблемой визуализации...). вопрос Акцентированные символы в Matplotlib имеет аналогичную проблему, поскольку он касается визуализации, а не самого значения

Я начинаю чувствовать себя немного потерянным... Я знаю, что у python 2.7 есть некоторая "трудность" юникода, но обычно я могу избежать их так или иначе.

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

text.replace(u'\u2212', '-')
# u'-1'

Но это больше и взломать, чем решение, и я почти уверен, что он не стабилен в разных системах, поэтому мне хотелось бы что-то ближе к решению.

Я работаю с

  • python 2.7.3
  • matplotlib 1.2.0
  • pylab 1.7.0
  • IPython 0.13.1

на Kubuntu 12.10.

Большое спасибо за вашу помощь!

EDIT:

Исправлен порядок сюжета, так как я получил перевернутый x и y, извините

EDIT2:

аналогичная информация присутствует по этой ссылке: http://www.coniferproductions.com/2012/12/17/unicode-character-dump-in-python/

в конце он показывает, как в некоторых книгах знак минус используется более эстетически приятным, но не распознанным интерпретатором python как допустимый символ.

EDIT3:

Загадка решена. персонаж, возвращающий matplotlib, является "МИНУС СИГНАЛОМ", т.е. правильным типографическим знаком для минуса. Тот, который создает keybord, фактически является "HYPHEN-MINUS", который обычно используется, но не является типографически правильным. см. в wikipedia для объяснения http://en.wikipedia.org/wiki/Hyphen-minus.

Итак, простая замена, которую я использовал, на самом деле является правильной практической задачей, но "этически" является ошибкой в ​​python (2.7 и 3.x), которые не распознают правильный символ для знака минус.

см. отслеживание ошибок в http://bugs.python.org/issue6632

EDIT4:

чтобы отключить это поведение, есть простое решение на matplotlib, просто измените rcparams либо в .matplotlibrc, либо программно.

import matplotlib as mpl
mpl.rcParams['axes.unicode_minus']=False
4b9b3361

Ответ 1

Все допустимые символы Юникода имеют имена. Мы можем проверить имя для распознанных числовых слов (DIGIT.keys()) и на этом основании заменить "обычные" числовые символы (DIGIT.values()) для данной метки юникода:

import matplotlib.pyplot as plt
import unicodedata as UD

DIGIT = {
    'MINUS': u'-',
    'ZERO': u'0',
    'ONE': u'1',
    'TWO': u'2',
    'THREE': u'3',
    'FOUR': u'4',
    'FIVE': u'5',
    'SIX': u'6',
    'SEVEN': u'7',
    'EIGHT': u'8',
    'NINE': u'9',
    'STOP': u'.'
    }

def guess(unistr):
    return ''.join([value for u in unistr
                    for key,value in DIGIT.iteritems()
                    if key in UD.name(u)])

fig, ax = plt.subplots(1)
ax.plot([-1, 0, 1, 2], range(4))
plt.savefig('/tmp/test.png')
labels = ax.get_xticklabels()
for label in labels:
    label = label.get_text()
    print(guess(label))

дает

-1.0
-0.5
0.0
0.5
1.0
1.5
2.0

Ответ 2

Используйте plt.xticks() вместо ax.get_xticklabels():

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1)
ax.plot([-1, 0, 1, 2], range(4))
plt.savefig('/tmp/test.png')
loc, labels = plt.xticks()
print(type(loc))
# <type 'numpy.ndarray'>
print(loc)
# [-1.  -0.5  0.   0.5  1.   1.5  2. ]