#python #matplotlib #formatting #kivy #typeerror
Вопрос:
Я создаю приложение, которое показывает продажи, достигнутые продавцом в разные периоды времени. Есть 4 флажка с разными периодами времени (День, Неделя, Месяц, Год). При нажатии на любой период будет показан график временной линии matplotlib.
Моя борьба начинается, когда я пытаюсь отформатировать y-тики как тысячи. Я использую format(value, ',')
команду. Однако я продолжаю получать TypeError: '<' not supported between instances of 'str' and 'int'
. После исследования я обнаружил, что предложение состоит в том, чтобы явно указать значение как int. Я пробовал это разными способами, но безуспешно. Например, если я добавлю int() для преобразования из строки в int: int(format(divider * 1, ','))
я получу ошибку ValueError: invalid literal for int() with base 10: '150,000'
Код для минимального воспроизводимого примера выглядит следующим образом:
Код на Python:
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import ButtonBehavior
from kivy.uix.label import Label
import matplotlib
import matplotlib.pyplot as plt
from datetime import datetime
matplotlib.use("module://kivy.garden.matplotlib.backend_kivy")
from kivy.garden.matplotlib import FigureCanvasKivyAgg
class LabelButton(ButtonBehavior, Label):
pass
class MainMenuWindow(Screen):
pass
class DashboardWindow(Screen):
# Valores Iniciales
""" PENDIENTE: HACER QUE LOS VALORES INICIALES VARÍEN EN FUNCIÓN DEL VENDEDOR Y SUS NÚMEROS """
meta_mensual = 300000
venta_diaria_ = 2500
venta_semanal_ = 50000
venta_mensual_ = 230000
venta_anual_ = 2500000
menu = None
number_items_mdlist = 0
minutes_items_mdlist = 0
duracion_actividades_list = []
picture_filepath_list = []
image_status_list = []
periodo_selec = ''
# Ventas
def diario_label_press(self, value):
if not self.ids.periodo_diario_.active:
self.ids.periodo_diario_.active = True
else:
self.ids.periodo_diario_.active = False
def semanal_label_press(self, value):
if not self.ids.periodo_semanal_.active:
self.ids.periodo_semanal_.active = True
else:
self.ids.periodo_semanal_.active = False
def mensual_label_press(self, value):
if not self.ids.periodo_mensual_.active:
self.ids.periodo_mensual_.active = True
else:
self.ids.periodo_mensual_.active = False
def anual_label_press(self, value):
if not self.ids.periodo_anual_.active:
self.ids.periodo_anual_.active = True
else:
self.ids.periodo_anual_.active = False
def switch_checkbox(self):
if self.ids.periodo_diario_.active:
# Meta de Ventas
meta_diaria = int(self.meta_mensual) / 30
meta_diaria_format = '{:,}'.format(meta_diaria)
self.ids.meta_label.text = "$ " str(meta_diaria_format) "0"
# Monto de Venta
venta = int(self.venta_diaria_)
venta = '{:,}'.format(venta)
self.ids.monto_venta.text = "$ " str(venta) ".00"
# Porcentaje de Meta
porcentaje = float(self.venta_diaria_ / meta_diaria)
porcentaje_format = "{0:.1%}".format(porcentaje)
self.ids.porcentaje_meta.text = str(porcentaje_format)
self.periodo_selec = 'Diario'
elif self.ids.periodo_semanal_.active:
# Meta de Ventas
meta_semanal = int(self.meta_mensual) / 4
meta_semanal_format = '{:,}'.format(meta_semanal)
self.ids.meta_label.text = "$ " str(meta_semanal_format) "0"
# Monto de Venta
venta = int(self.venta_semanal_)
venta = '{:,}'.format(venta)
self.ids.monto_venta.text = "$ " str(venta) ".00"
# Porcentaje de Meta
porcentaje = float(self.venta_semanal_ / meta_semanal)
porcentaje_format = "{0:.1%}".format(porcentaje)
self.ids.porcentaje_meta.text = str(porcentaje_format)
self.periodo_selec = 'Semanal'
elif self.ids.periodo_mensual_.active:
# Meta de Ventas
meta_mensual = int(self.meta_mensual) * 1
meta_mensual_format = '{:,}'.format(self.meta_mensual)
self.ids.meta_label.text = "$ " str(meta_mensual_format) ".00"
# Monto de Venta
venta = int(self.venta_mensual_)
venta = '{:,}'.format(venta)
self.ids.monto_venta.text = "$ " str(venta) ".00"
# Porcentaje de Meta
porcentaje = float(self.venta_mensual_ / self.meta_mensual)
porcentaje_format = "{0:.1%}".format(porcentaje)
self.ids.porcentaje_meta.text = str(porcentaje_format)
self.periodo_selec = 'Mensual'
elif self.ids.periodo_anual_.active:
# Meta de Ventas
meta_anual = int(self.meta_mensual) * 12
meta_anual_format = '{:,}'.format(meta_anual)
self.ids.meta_label.text = "$ " str(meta_anual_format) ".00"
# Monto de Venta
venta = int(self.venta_anual_)
venta = '{:,}'.format(venta)
self.ids.monto_venta.text = "$ " str(venta) ".00"
# Porcentaje de Meta
porcentaje = float(self.venta_anual_ / meta_anual)
porcentaje_format = "{0:.1%}".format(porcentaje)
self.ids.porcentaje_meta.text = str(porcentaje_format)
self.periodo_selec = 'Anual'
elif not self.ids.periodo_diario_.active and not self.ids.periodo_semanal_.active and
not self.ids.periodo_mensual_.active and not self.ids.periodo_anual_.active:
# Meta de Ventas
self.ids.meta_label.text = "Seleccione una opción"
self.ids.monto_venta.text = "$ 0.00"
self.ids.porcentaje_meta.text = "0.00 %"
self.actualizar_timeline_ventas(self.periodo_selec)
def actualizar_timeline_ventas(self, selec):
now = datetime.now()
dash = MDApp.get_running_app().root.get_screen('dash')
plt.clf()
plt.style.use('seaborn')
mes = ''
if now.month == 1:
mes = 'Enero'
elif now.month == 2:
mes = 'Febrero'
elif now.month == 3:
mes = 'Marzo'
elif now.month == 4:
mes = 'Abril'
elif now.month == 5:
mes = 'Mayo'
elif now.month == 6:
mes = 'Junio'
elif now.month == 7:
mes = 'Julio'
elif now.month == 8:
mes = 'Agosto'
elif now.month == 9:
mes = 'Septiembre'
elif now.month == 10:
mes = 'Octubre'
elif now.month == 11:
mes = 'Noviembre'
elif now.month == 12:
mes = 'Diciembre'
# Línea de tiempo del día
if self.ids.periodo_diario_.active:
dia_semana = ''
if now.weekday() == 0:
dia_semana = 'Lunes'
elif now.weekday() == 1:
dia_semana = 'Martes'
elif now.weekday() == 2:
dia_semana = 'Miércoles'
elif now.weekday() == 3:
dia_semana = 'Jueves'
elif now.weekday() == 4:
dia_semana = 'Viernes'
elif now.weekday() == 5:
dia_semana = 'Sábado'
elif now.weekday() == 6:
dia_semana = 'Domingo'
dash.ids.momento_seleccionado.text = f'Hoy, {dia_semana.lower()}, {now.day} de {mes.lower()} del {now.year}'
dash.ids.timeline_container.clear_widgets()
hora = ['9:00 a.m.', '10:00 a.m.', '11:00 a.m.', '12:00 p.m.', '1:00 p.m.', '2:00 p.m.', '3:00 p.m.',
'4:00 p.m.', '5:00 p.m.', '6:00 p.m.']
venta = [0, 1500, 10000, 17000, 2600, 0, 0, 2710, 7500, 0]
divider = max(venta) / 4
plt.yticks([0, divider * 1, divider * 2, divider * 3, divider * 4])
plt.plot(hora, venta, color='blue', marker='D')
plt.xticks(['9:00 a.m.', '12:00 p.m.', '3:00 p.m.', '6:00 p.m.'])
plt.grid(True)
dash.ids.timeline_container.add_widget(FigureCanvasKivyAgg(figure=plt.gcf(), size_hint=(0.95, 0.95),
pos_hint={'center_x': 0.5, 'top': 1}))
# Línea de tiempo Semanal
elif self.ids.periodo_semanal_.active:
dash.ids.momento_seleccionado.text = f'Semana {datetime.date(now).isocalendar()[1]} del {now.year}'
dash.ids.timeline_container.clear_widgets()
dia = ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab', 'Dom']
venta = [10000, 12000, 22000, 17000, 26000, 5000, 0]
divider = max(venta) / 4
plt.yticks([0, divider * 1, divider * 2, divider * 3, divider * 4])
plt.plot(dia, venta, color='yellow', marker='o')
plt.grid(True)
dash.ids.timeline_container.add_widget(FigureCanvasKivyAgg(figure=plt.gcf(), size_hint=(0.95, 0.95),
pos_hint={'center_x': 0.5, 'top': 1}))
# Línea de tiempo mensual
elif self.ids.periodo_mensual_.active:
dash.ids.momento_seleccionado.text = f'{mes} {now.year}'
dash.ids.timeline_container.clear_widgets()
now = datetime.now()
curr_month = now.now().month
if int(curr_month) == 1 or int(curr_month) == 3 or int(curr_month) == 5 or int(curr_month) == 7
or int(curr_month) == 8 or int(curr_month) == 10 or int(curr_month) == 12:
number_days = 31
elif int(curr_month) == 4 or int(curr_month) == 6 or int(curr_month) == 9 or int(curr_month) == 11:
number_days = 30
else:
number_days = 28
import random
dia = []
venta = []
day = 1
for i in range(number_days):
dia.append(day)
venta.append(random.randrange(150000, 750000))
day = 1
divider = max(venta) / 4
plt.yticks([0, divider * 1, divider * 2, divider * 3, divider * 4])
plt.plot(dia, venta, color='red', marker='s')
plt.grid(True)
dash.ids.timeline_container.add_widget(FigureCanvasKivyAgg(figure=plt.gcf(), size_hint=(0.95, 0.95),
pos_hint={'center_x': 0.5, 'top': 1}))
# Línea de tiempo Anual
elif self.ids.periodo_anual_.active:
dash.ids.momento_seleccionado.text = str(now.year)
dash.ids.timeline_container.clear_widgets()
mes = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic']
venta = [100000, 120000, 130000, 170000, 260000, 220000, 243000, 271000, 274000, 320000, 430000, 600000]
divider = int(max(venta) / 4)
plt.yticks([int(0), format(divider * 1, ','), format(divider * 2, ','), format(divider * 3, ','),
format(divider * 4, ',')])
plt.plot(mes, venta, color='green', marker='^')
plt.grid(True)
dash.ids.timeline_container.add_widget(FigureCanvasKivyAgg(figure=plt.gcf(), size_hint=(0.95, 0.95),
pos_hint={'center_x': 0.5, 'top': 1}))
else:
dash.ids.momento_seleccionado.text = 'Líneas de tiempo'
dash.ids.timeline_container.clear_widgets()
dash.ids.timeline_container.add_widget(self.ids.label_timeline_vacio)
class WindowManager(ScreenManager):
pass
class Linegraph(MDApp):
def build(self):
self.theme_cls.primary_palette = "Teal"
return WindowManager()
if __name__ == "__main__":
Linegraph().run()
Код КВ:
<WindowManager>:
id: screen_manager
DashboardWindow:
id: dash
name: 'dash'
<DashboardWindow>:
id: dash
name:'dash'
MDBoxLayout:
orientation: 'vertical'
valign: 'middle'
padding: '10dp'
MDLabel:
text: "Nombre del Vendedor"
size_hint_y: 0.15
font_size: (root.width**2 root.height**2) / 12.75**4
valign: 'middle'
bold: True
MDBoxLayout:
orientation: 'horizontal'
spacing: '5dp'
size_hint_y: 0.15
MDLabel:
text: "Meta de venta:"
valign: 'middle'
font_size: (root.width**2 root.height**2) / 14.25**4
MDLabel:
id: meta_label
text: 'Seleccione una opcion'
valign: 'middle'
font_size: (root.width**2 root.height**2) / 14.25**4
bold: True
MDBoxLayout:
orientation: 'horizontal'
spacing: '5dp'
size_hint_y: 0.20
MDCheckbox:
group: 'periodo_venta'
id: periodo_diario_
size_hint: None, None
size: dp(50), dp(50)
pos_hint: {"x":0, "center_y":0.5}
on_active:
root.switch_checkbox()
LabelButton:
text: 'Diario'
color: 0, 0, 0, 1
halign: 'left'
valign: 'middle'
text_size: self.size
on_release: root.diario_label_press(root.ids.periodo_diario_.active)
MDCheckbox:
group: 'periodo_venta'
id: periodo_semanal_
size_hint: None, None
size: dp(50), dp(50)
pos_hint: {"x":0, "center_y":0.5}
on_active:
root.switch_checkbox()
LabelButton:
text: 'Semanal'
color: 0, 0, 0, 1
halign: 'left'
valign: 'middle'
text_size: self.size
on_release: root.semanal_label_press(root.ids.periodo_semanal_.active)
MDCheckbox:
group: 'periodo_venta'
id: periodo_mensual_
size_hint: None, None
size: dp(50), dp(50)
pos_hint: {"x":0, "center_y":0.5}
on_active:
root.switch_checkbox()
LabelButton:
text: 'Mensual'
color: 0, 0, 0, 1
halign: 'left'
valign: 'middle'
text_size: self.size
on_release: root.mensual_label_press(root.ids.periodo_mensual_.active)
MDCheckbox:
group: 'periodo_venta'
id: periodo_anual_
size_hint: None, None
size: dp(50), dp(50)
pos_hint: {"x":0, "center_y":0.5}
on_active:
root.switch_checkbox()
LabelButton:
text: 'Anual'
color: 0, 0, 0, 1
halign: 'left'
valign: 'middle'
text_size: self.size
on_release: root.anual_label_press(root.ids.periodo_anual_.active)
MDBoxLayout:
orientation: 'horizontal'
padding: 0, '10dp', 0, 0
spacing: '20dp'
size_hint: 1, 0.25
MDCard:
orientation: 'vertical'
padding: '10dp'
size_hint: 0.4, 1
radius: [16, ]
md_bg_color: [1, 1, 1, 0.85]
MDLabel:
id: monto_venta
text: "$ 0.00"
theme_text_color: "Custom"
text_color: 0, 0, 0, 1
font_style: 'H1'
halign: 'center'
font_size: (root.width**2 root.height**2) / 13**4
MDLabel:
text: "Monto Vendido"
theme_text_color: "Custom"
text_color: 0, 0, 0, 1
font_style: 'Subtitle2'
halign: 'center'
MDCard:
size_hint: 0.4, 1
orientation: 'vertical'
padding: '10dp'
radius: [16, ]
md_bg_color: [1, 1, 1, 0.85]
MDLabel:
id: porcentaje_meta
text: '0.00 %'
theme_text_color: "Custom"
text_color: 0, 0, 0, 1
font_style: 'H1'
halign: 'center'
font_size: (root.width**2 root.height**2) / 13**4
MDLabel:
text: "% de meta alcanzado"
theme_text_color: "Custom"
text_color: 0, 0, 0, 1
font_style: 'Subtitle2'
halign: 'center'
MDBoxLayout:
orientation: 'horizontal'
spacing: '10dp'
# padding: "10dp", 0,"10dp", "10dp"
size_hint_y: 0.45
MDCard:
size_hint: 1, 0.9
orientation: 'vertical'
radius: [16, ]
MDBoxLayout:
size_hint: 1, 0.175
radius: [16, 16, 0, 0]
md_bg_color: 0.114, 0.212, 0.235, 1
MDLabel:
id: momento_seleccionado
text: 'Lineas de tiempo'
halign: 'center'
theme_text_color: "Custom"
text_color: 1, 1, 1, 1
MDSeparator:
height: "2dp"
color: 0.95, 0.81, 0.25, 1
MDBoxLayout:
id: timeline_container
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
radius: [0, 0, 16, 16]
MDLabel:
id: label_timeline_vacio
text: 'Seleccionar temporalidad para visualizar ventas del periodo'
halign: 'center'
Как вы можете видеть, единственный флажок, который пытается выполнить форматирование тысячи, — это «Год». Таким образом, это будет единственная кнопка, которая приведет к сбою приложения. Обратите внимание, что причина, по которой я использовал переменную делителя, заключается в том, чтобы получить 5 равномерно распределенных тиков. Есть какие-либо предложения о том, как я могу правильно форматировать галочки?
Во второй части вопроса я буду очень признателен за предложения о том, как добавлять всплывающие метки, которые отображаются при нажатии на разные точки графиков. Я нашел такое, но только на пакете Фолиума для карт. Существуют ли какие-либо аналогичные решения для matplotlib?
Заранее большое спасибо.
Ответ №1:
Я верю, что у вас проблема с линией:
plt.yticks([int(0), format(divider * 1, ','), format(divider * 2, ','), format(divider * 3, ','), format(divider * 4, ',')])
yticks()
Метод принимает ноль, один или два аргумента. Если вы предоставляете только один аргумент, как в приведенной выше строке, то это должен быть массив чисел, а не строк. Если вы хотите указать фактические метки, то необходимо указать два аргумента — список чисел и другой список строк. Итак, я считаю, что приведенная выше строка должна быть:
plt.yticks([0, divider * 1, divider * 2, divider * 3, divider * 4], ['0', format(divider * 1, ','), format(divider * 2, ','), format(divider * 3, ','), format(divider * 4, ',')])
Комментарии:
1. Большое спасибо за ваш ответ, Джон. Ты был прав. Ваше решение сработало именно так, как ожидалось. У вас есть какие-либо замечания или предложения по моей второй части вопроса? На всплывающих ярлыках? Есть ли модуль, который выполняет эту работу в matplotlib, как в Folium? Еще раз большое спасибо.
2. Мое лучшее предположение для всплывающих меток состояло бы в том, чтобы расширить
FigureCanvasKivyAgg
и реализоватьon_touch_down()
метод, который выполняет всплывающие окна. Ничего из того, что я знаю о готовых к использованию пакетах.