ChannelPainter.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. from .DataStream import EChPosmap as pos_map, day_stamp, span_days, time_border, calc_interval
  2. from .ChannelReader import ChannelReader
  3. from collections import defaultdict
  4. from matplotlib.figure import Figure
  5. from matplotlib import ticker
  6. from io import BytesIO
  7. import numpy as np
  8. from .algorithm import calc_chratios
  9. import time as time
  10. import logging
  11. logger = logging.getLogger('painter')
  12. _all_channels = set()
  13. def add_channel(channel):
  14. if channel not in _all_channels:
  15. _all_channels.add(channel)
  16. def get_channels():
  17. return list(_all_channels)
  18. def ratio_pathes(reader: ChannelReader, tuple_pathes: dict, days: list, spec=None):
  19. count = len(days)
  20. show_detail = True if len(list(tuple_pathes.keys())) == 1 else False
  21. if show_detail == False:
  22. all_datas = reader.init_data(count)
  23. else:
  24. all_datas = None
  25. for name, tup in tuple_pathes.items():
  26. add_channel(name)
  27. mch_datas = reader.init_data(count)
  28. for _card_type, _spec in tup:
  29. if spec is not None and _spec != spec:
  30. continue
  31. if show_detail:
  32. detail_datas = reader.init_data(count)
  33. else:
  34. detail_datas = None
  35. for i, day in enumerate(days):
  36. data = reader.read(day, name, _card_type, _spec)
  37. if data is not None:
  38. column_pos = i * 86400
  39. view = mch_datas[:, column_pos:column_pos + 86400]
  40. view += data
  41. if show_detail:
  42. view = detail_datas[:, column_pos:column_pos + 86400]
  43. view += data
  44. if show_detail:
  45. yield name, _card_type, _spec, detail_datas
  46. if all_datas is not None:
  47. all_datas += mch_datas
  48. yield name, None, None, mch_datas
  49. if show_detail == False:
  50. yield 'all', None, None, all_datas
  51. class ChannelPainter(object):
  52. def __init__(self, start_time: int, end_time: int, chnames: set = None, card_types: set = None, spec: int = None, filter_wave: int = None):
  53. self._reader = ChannelReader()
  54. _start_time, _end_time, self._chnames, self._card_types, self._spec, self._filter_wave = start_time, end_time, chnames, card_types, spec, filter_wave
  55. if _end_time is None:
  56. _end_time = int(time.time())
  57. end_time = self._reader.near_stamp(_end_time, False)
  58. if end_time is None:
  59. raise Exception('data is empty')
  60. if _start_time is None or start_time > end_time:
  61. _start_time = end_time - 7200
  62. start_time = self._reader.near_stamp(_start_time, True)
  63. if start_time is None:
  64. raise Exception('data is empty')
  65. stime = lambda t: time.strftime('%d-%H:%M:%S', time.localtime(t))
  66. logger.debug("start_time=%s end_time=%s",stime(start_time) ,stime(end_time))
  67. interval = calc_interval(start_time, end_time)
  68. start_time = time_border(interval, start_time, True)
  69. end_time = time_border(interval, end_time, False)
  70. self._days = span_days(start_time, end_time)
  71. self._start_time = start_time
  72. self._end_time = end_time
  73. self._interval = interval
  74. pass
  75. def _fig_funs(self):
  76. def create():
  77. fig = Figure(figsize=(19, 8))
  78. ax = fig.subplots()
  79. ax.set_title('success ratio')
  80. ax.set(xlabel='time', ylabel='ratio')
  81. return ax, fig
  82. def flush(ax, fig, ticks, lables):
  83. ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=4))
  84. ax.set_xticks(ticks=ticks)
  85. ax.set_xticklabels(lables)
  86. fig.autofmt_xdate()
  87. ax.grid()
  88. fig.subplots_adjust(left=0.1, right=0.8, top=0.95, bottom=0.1)
  89. ax.legend(bbox_to_anchor=(1, 1), loc='upper left')
  90. buf = BytesIO()
  91. fig.savefig(buf, format="png")
  92. return buf
  93. return create, flush
  94. def paint(self):
  95. reader = ChannelReader()
  96. tuple_pathes = reader.many_tuple_path(self._days, self._chnames, self._card_types, self._spec)
  97. gen = ratio_pathes(self._reader, tuple_pathes, self._days, self._spec)
  98. if len(self._days) == 0:
  99. return BytesIO()
  100. day_stamp = self._days[0]
  101. fig_create, fig_flush = self._fig_funs()
  102. ax, fig = fig_create()
  103. x = np.array([d - self._start_time for d in range(self._start_time, self._end_time)])
  104. if self._filter_wave is not None and self._filter_wave > 1:
  105. window = np.ones(self._filter_wave) / self._filter_wave
  106. else:
  107. window = None
  108. chname_ratios = []
  109. for _chname, _card_type, _spec, _data in gen:
  110. succ, count, y = calc_chratios(_data, pos_map, self._start_time - day_stamp, self._end_time - day_stamp)
  111. y = np.convolve(y, window, 'same') if window is not None else y
  112. label, ratio = self._label(chname=_chname, succ=succ, count=count, card_type=_card_type, spec=_spec)
  113. ax.plot(x, y, ls='-', label=label)
  114. if _card_type is None and _spec is None and _chname != 'all':
  115. chname_ratios.append((_chname, ratio))
  116. ticks = [d - self._start_time for d in range(self._start_time, self._end_time + 1, self._interval)]
  117. xlables = [time.strftime('%d-%H:%M:%S', time.localtime(d + self._start_time)) for d in ticks]
  118. buf = fig_flush(ax, fig, ticks, xlables)
  119. chname_ratios = sorted(chname_ratios, key=lambda x: (x[1], x[0]), reverse=True)
  120. result = []
  121. for name, ratio in chname_ratios:
  122. result.append(f'{name}:{ratio}')
  123. return buf, result
  124. def _label(self, chname, succ, count, card_type=None, spec=None):
  125. _card_type = None
  126. if card_type == 1:
  127. _card_type = 'SY'
  128. elif card_type == 2:
  129. _card_type = 'SH'
  130. elif card_type == 4:
  131. _card_type = 'YD'
  132. elif card_type == 5:
  133. _card_type = 'LT'
  134. elif card_type == 6:
  135. _card_type = 'DX'
  136. elif card_type == 7:
  137. _card_type = 'TH'
  138. lable = f"{chname}"
  139. if _card_type is not None:
  140. lable += f"-{_card_type}"
  141. if spec is not None:
  142. lable += f"-{spec}"
  143. if count > 0:
  144. ratio = round(succ * 100 / count, 2)
  145. else:
  146. ratio = 0.00
  147. lable += f":{succ}/{count}={ratio}%"
  148. return lable, ratio