<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          利用Python制作可以動(dòng)起來的圖表!

          共 27565字,需瀏覽 56分鐘

           ·

          2021-05-29 00:38

          通常大家做出來的圖表,絕大部分都是靜態(tài)的,有時(shí)會(huì)顯得不夠吸引人。


          今天給大家介紹一下,如何用Python繪制動(dòng)態(tài)圖表。


          主要是使用到Matplotlib+imageio,其中Matplotlib就有一個(gè)Animation類,可以生成動(dòng)圖GIF,不過使用起來學(xué)習(xí)成本較高,還是有一定難度的。


          這里我將先創(chuàng)建靜態(tài)圖表的圖片,然后使用Imageio創(chuàng)建一個(gè)GIF(動(dòng)態(tài)圖表)。


          一共給大家介紹三種動(dòng)態(tài)圖表的繪制,折線圖,條形圖,散點(diǎn)圖。



          01 折線圖


          先來繪制一個(gè)簡(jiǎn)單的折線圖看看。


          import os
          import numpy as np
          import matplotlib.pyplot as plt
          import imageio

          # 生成40個(gè)取值在30-40的數(shù)
          y = np.random.randint(3040, size=(40))
          # 繪制折線
          plt.plot(y)
          # 設(shè)置y軸最小值和最大值
          plt.ylim(2050)

          # 顯示
          plt.show()


          使用Numpy創(chuàng)建一個(gè)數(shù)值范圍在30到40之間的隨機(jī)整數(shù)列表,結(jié)果如下。



          下面將對(duì)整數(shù)列表進(jìn)行切片,生成不同階段的圖表。


          # 第一張圖
          plt.plot(y[:-3])
          plt.ylim(2050)
          plt.savefig('1.png')
          plt.show()

          # 第二張圖
          plt.plot(y[:-2])
          plt.ylim(2050)
          plt.savefig('2.png')
          plt.show()

          # 第三張圖
          plt.plot(y[:-1])
          plt.ylim(2050)
          plt.savefig('3.png')
          plt.show()

          # 第四張圖
          plt.plot(y)
          plt.ylim(2050)
          plt.savefig('4.png')
          plt.show()


          得到x軸為0:36、0:37、0:38、0:39四個(gè)折線圖表。



          有了這四張圖,我們就可以使用Imageio生成GIF了。


          # 生成Gif
          with imageio.get_writer('mygif.gif', mode='I'as writer:
              for filename in ['1.png''2.png''3.png''4.png']:
                  image = imageio.imread(filename)
                  writer.append_data(image)


          動(dòng)圖來了。



          一個(gè)會(huì)動(dòng)的折線圖表就制作出來了,過不是從x軸坐標(biāo)為0的時(shí)候開始的。


          filenames = []
          num = 0
          for i in y:
              num += 1
              # 繪制40張折線圖
              plt.plot(y[:num])
              plt.ylim(2050)

              # 保存圖片文件
              filename = f'{num}.png'
              filenames.append(filename)
              plt.savefig(filename)
              plt.close()

          # 生成gif
          with imageio.get_writer('mygif.gif', mode='I'as writer:
              for filename in filenames:
                  image = imageio.imread(filename)
                  writer.append_data(image)

          # 刪除40張折線圖
          for filename in set(filenames):
              os.remove(filename)


          繪制出40張折線圖,并且保存圖片,生成GIF。


          可以看到折線圖的x坐標(biāo)從0一直到了40。


          02 條形圖


          上面的折線圖每次只有一個(gè)y值即可,而條形圖則需要所有的y值,如此所有的條形才能同時(shí)移動(dòng)。

          給X軸創(chuàng)建固定值,Y軸創(chuàng)建列表,并使用Matplotlib的條形圖函數(shù)。

          x = [12345]
          coordinates_lists = [[00000],
                               [1030603010],
                               [7040204070],
                               [1020304050],
                               [5040302010],
                               [75075075],
                               [00000]]
          filenames = []
          for index, y in enumerate(coordinates_lists):
              # 條形圖
              plt.bar(x, y)
              plt.ylim(080)

              # 保存圖片文件
              filename = f'{index}.png'
              filenames.append(filename)

              # 重復(fù)最后一張圖形15幀(數(shù)值都為0),15張圖片
              if (index == len(coordinates_lists) - 1):
                  for i in range(15):
                      filenames.append(filename)

              # 保存
              plt.savefig(filename)
              plt.close()

          # 生成gif
          with imageio.get_writer('mygif.gif', mode='I'as writer:
              for filename in filenames:
                  image = imageio.imread(filename)
                  writer.append_data(image)

          # 刪除20張柱狀圖
          for filename in set(filenames):
              os.remove(filename)


          有數(shù)值的條形圖圖片是5張,沒數(shù)值的圖片是2+15=17張。


          GIF結(jié)束段,添加了15幀空白圖片。所以在結(jié)束的時(shí)候會(huì)顯示一段時(shí)間的空白。

          可以設(shè)置一下條形圖當(dāng)前位置到下個(gè)位置的速度,讓過渡變得平滑

          將當(dāng)前位置和下一個(gè)位置之間的距離除以過渡幀數(shù)。

          n_frames = 10
          x = [12345]
          coordinates_lists = [[00000],
                               [1030603010],
                               [7040204070],
                               [1020304050],
                               [5040302010],
                               [75075075],
                               [00000]]
          print('生成圖表\n')
          filenames = []
          for index in np.arange(0, len(coordinates_lists) - 1):
              # 獲取當(dāng)前圖像及下一圖像的y軸坐標(biāo)值
              y = coordinates_lists[index]
              y1 = coordinates_lists[index + 1]

              # 計(jì)算當(dāng)前圖像與下一圖像y軸坐標(biāo)差值
              y_path = np.array(y1) - np.array(y)
              for i in np.arange(0, n_frames + 1):
                  # 分配每幀的y軸移動(dòng)距離
                  # 逐幀增加y軸的坐標(biāo)值
                  y_temp = (y + (y_path / n_frames) * i)
                  # 繪制條形圖
                  plt.bar(x, y_temp)
                  plt.ylim(080)
                  # 保存每一幀的圖像
                  filename = f'images/frame_{index}_{i}.png'
                  filenames.append(filename)
                  # 最后一幀重復(fù),畫面停留一會(huì)
                  if (i == n_frames):
                      for i in range(5):
                          filenames.append(filename)
                  # 保存圖片
                  plt.savefig(filename)
                  plt.close()
          print('保存圖表\n')
          # 生成GIF
          print('生成GIF\n')
          with imageio.get_writer('mybars.gif', mode='I'as writer:
              for filename in filenames:
                  image = imageio.imread(filename)
                  writer.append_data(image)
          print('保存GIF\n')
          print('刪除圖片\n')
          # 刪除圖片
          for filename in set(filenames):
              os.remove(filename)
          print('完成')


          看起來是平滑了許多。


          好了,接下來我們更改一下圖表相關(guān)的配置參數(shù),讓圖表變得好看。

          n_frames = 10
          bg_color = '#95A4AD'
          bar_color = '#283F4E'
          gif_name = 'bars'
          x = [12345]
          coordinates_lists = [[00000],
                               [1030603010],
                               [7040204070],
                               [1020304050],
                               [5040302010],
                               [75075075],
                               [00000]]
          print('生成圖表\n')
          filenames = []
          for index in np.arange(0, len(coordinates_lists) - 1):
              y = coordinates_lists[index]
              y1 = coordinates_lists[index + 1]
              y_path = np.array(y1) - np.array(y)
              for i in np.arange(0, n_frames + 1):
                  y_temp = (y + (y_path / n_frames) * i)
                  # 繪制條形圖
                  fig, ax = plt.subplots(figsize=(84))
                  ax.set_facecolor(bg_color)
                  plt.bar(x, y_temp, width=0.4, color=bar_color)
                  plt.ylim(080)
                  # 移除圖表的上邊框和右邊框
                  ax.spines['right'].set_visible(False)
                  ax.spines['top'].set_visible(False)
                  # 設(shè)置虛線網(wǎng)格線
                  ax.set_axisbelow(True)
                  ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
                  # 保存每一幀的圖像
                  filename = f'images/frame_{index}_{i}.png'
                  filenames.append(filename)

                  # 最后一幀重復(fù),畫面停留一會(huì)
                  if (i == n_frames):
                      for i in range(5):
                          filenames.append(filename)
                  # 保存圖片
                  plt.savefig(filename, dpi=96, facecolor=bg_color)
                  plt.close()
          print('保存圖表\n')
          # 生成GIF
          print('生成GIF\n')
          with imageio.get_writer(f'{gif_name}.gif', mode='I'as writer:
              for filename in filenames:
                  image = imageio.imread(filename)
                  writer.append_data(image)
          print('保存GIF\n')
          print('刪除圖片\n')
          # 刪除圖片
          for filename in set(filenames):
              os.remove(filename)
          print('完成')


          給圖表添加了背景色、條形圖上色、去除邊框、增加網(wǎng)格線等。


          看起來,效果還不錯(cuò)!

          當(dāng)然也有一些值得改進(jìn)的地方,比如添加標(biāo)題。通過插值的方式來使過渡變得更平滑,甚至可以讓條形圖在x軸上移動(dòng)。

          這里大家就可以自行去研究啦。


          03 散點(diǎn)圖

          要繪制動(dòng)態(tài)散點(diǎn)圖,則需要同時(shí)考慮x軸和y軸的值。

          這里不一定要在每幀上顯示相同數(shù)量的點(diǎn),因此需要對(duì)其進(jìn)行校正來進(jìn)行過渡。

          coordinates_lists = [[[0], [0]],
                               [[100200300], [100200300]],
                               [[400500600], [400500600]],
                               [[400500600400500600], [400500600600500400]],
                               [[500], [500]],
                               [[0], [0]]]
          gif_name = 'movie'
          n_frames = 10
          bg_color = '#95A4AD'
          marker_color = '#283F4E'
          marker_size = 25
          print('生成圖表\n')
          filenames = []
          for index in np.arange(0, len(coordinates_lists) - 1):
              # 獲取當(dāng)前圖像及下一圖像的x與y軸坐標(biāo)值
              x = coordinates_lists[index][0]
              y = coordinates_lists[index][1]
              x1 = coordinates_lists[index + 1][0]
              y1 = coordinates_lists[index + 1][1]
              # 查看兩點(diǎn)差值
              while len(x) < len(x1):
                  diff = len(x1) - len(x)
                  x = x + x[:diff]
                  y = y + y[:diff]
              while len(x1) < len(x):
                  diff = len(x) - len(x1)
                  x1 = x1 + x1[:diff]
                  y1 = y1 + y1[:diff]
              # 計(jì)算路徑
              x_path = np.array(x1) - np.array(x)
              y_path = np.array(y1) - np.array(y)
              for i in np.arange(0, n_frames + 1):
                  # 計(jì)算當(dāng)前位置
                  x_temp = (x + (x_path / n_frames) * i)
                  y_temp = (y + (y_path / n_frames) * i)
                  # 繪制圖表
                  fig, ax = plt.subplots(figsize=(66), subplot_kw=dict(aspect="equal"))
                  ax.set_facecolor(bg_color)

                  plt.scatter(x_temp, y_temp, c=marker_color, s=marker_size)
                  plt.xlim(01000)
                  plt.ylim(01000)
                  # 移除邊框線
                  ax.spines['right'].set_visible(False)
                  ax.spines['top'].set_visible(False)
                  # 網(wǎng)格線
                  ax.set_axisbelow(True)
                  ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
                  ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
                  # 保存圖片
                  filename = f'images/frame_{index}_{i}.png'
                  filenames.append(filename)
                  if (i == n_frames):
                      for i in range(5):
                          filenames.append(filename)
                  # 保存
                  plt.savefig(filename, dpi=96, facecolor=bg_color)
                  plt.close()
          print('保存圖表\n')
          # 生成GIF
          print('生成GIF\n')
          with imageio.get_writer(f'{gif_name}.gif', mode='I'as writer:
              for filename in filenames:
                  image = imageio.imread(filename)
                  writer.append_data(image)
          print('保存GIF\n')
          print('刪除圖片\n')
          # 刪除圖片
          for filename in set(filenames):
              os.remove(filename)
          print('完成')

          效果如下。


          當(dāng)然還有更有趣的散點(diǎn)圖變化,比如字母變化。

          使用OpenCV從圖像創(chuàng)建mask,繪制填充有隨機(jī)x/y坐標(biāo)的圖,并過濾mask內(nèi)的點(diǎn)。 

          使用Matplotlib繪制散點(diǎn)圖,使用ImageIO生成gif。

          import os
          import numpy as np
          import matplotlib.pyplot as plt
          import imageio
          import random
          import cv2


          # 根據(jù)字母的形狀, 將字母轉(zhuǎn)化為多個(gè)隨機(jī)點(diǎn)
          def get_masked_data(letter, intensity=2):
              # 多個(gè)隨機(jī)點(diǎn)填充字母
              random.seed(420)
              x = []
              y = []

              for i in range(intensity):
                  x = x + random.sample(range(01000), 500)
                  y = y + random.sample(range(01000), 500)

              if letter == ' ':
                  return x, y

              # 獲取圖片的mask
              mask = cv2.imread(f'images/letters/{letter.upper()}.png'0)
              mask = cv2.flip(mask, 0)

              # 檢測(cè)點(diǎn)是否在mask中
              result_x = []
              result_y = []
              for i in range(len(x)):
                  if (mask[y[i]][x[i]]) == 0:
                      result_x.append(x[i])
                      result_y.append(y[i])

              # 返回x,y
              return result_x, result_y


          # 將文字切割成一個(gè)個(gè)字母
          def text_to_data(txt, repeat=True, intensity=2):
              print('將文本轉(zhuǎn)換為數(shù)據(jù)\n')
              letters = []
              for i in txt.upper():
                  letters.append(get_masked_data(i, intensity=intensity))
              # 如果repeat為1時(shí),重復(fù)第一個(gè)字母
              if repeat:
                  letters.append(get_masked_data(txt[0], intensity=intensity))
              return letters


          def build_gif(coordinates_lists, gif_name='movie', n_frames=10, bg_color='#95A4AD',
                        marker_color='#283F4E', marker_size=25)
          :

              print('生成圖表\n')
              filenames = []
              for index in np.arange(0, len(coordinates_lists) - 1):
                  # 獲取當(dāng)前圖像及下一圖像的x與y軸坐標(biāo)值
                  x = coordinates_lists[index][0]
                  y = coordinates_lists[index][1]

                  x1 = coordinates_lists[index + 1][0]
                  y1 = coordinates_lists[index + 1][1]

                  # 查看兩點(diǎn)差值
                  while len(x) < len(x1):
                      diff = len(x1) - len(x)
                      x = x + x[:diff]
                      y = y + y[:diff]

                  while len(x1) < len(x):
                      diff = len(x) - len(x1)
                      x1 = x1 + x1[:diff]
                      y1 = y1 + y1[:diff]

                  # 計(jì)算路徑
                  x_path = np.array(x1) - np.array(x)
                  y_path = np.array(y1) - np.array(y)

                  for i in np.arange(0, n_frames + 1):
                      # 計(jì)算當(dāng)前位置
                      x_temp = (x + (x_path / n_frames) * i)
                      y_temp = (y + (y_path / n_frames) * i)

                      # 繪制圖表
                      fig, ax = plt.subplots(figsize=(66), subplot_kw=dict(aspect="equal"))
                      ax.set_facecolor(bg_color)
                      plt.xticks([])  # 去掉x軸
                      plt.yticks([])  # 去掉y軸
                      plt.axis('off')  # 去掉坐標(biāo)軸

                      plt.scatter(x_temp, y_temp, c=marker_color, s=marker_size)

                      plt.xlim(01000)
                      plt.ylim(01000)

                      # 移除框線
                      ax.spines['right'].set_visible(False)
                      ax.spines['top'].set_visible(False)

                      # 網(wǎng)格線
                      ax.set_axisbelow(True)
                      ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.7)
                      ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.7)

                      # 保存圖片
                      filename = f'images/frame_{index}_{i}.png'

                      if (i == n_frames):
                          for i in range(5):
                              filenames.append(filename)

                      filenames.append(filename)

                      # 保存
                      plt.savefig(filename, dpi=96, facecolor=bg_color)
                      plt.close()
              print('保存圖表\n')
              # 生成GIF
              print('生成GIF\n')
              with imageio.get_writer(f'{gif_name}.gif', mode='I'as writer:
                  for filename in filenames:
                      image = imageio.imread(filename)
                      writer.append_data(image)
              print('保存GIF\n')
              print('刪除圖片\n')
              # 刪除圖片
              for filename in set(filenames):
                  os.remove(filename)

              print('完成')


          coordinates_lists = text_to_data('Python', repeat=True, intensity=50)

          build_gif(coordinates_lists,
                    gif_name='Python',
                    n_frames=7,
                    bg_color='#52A9F0',
                    marker_color='#000000',
                    marker_size=0.2)

          生成一個(gè)Python單詞字母的動(dòng)態(tài)散點(diǎn)圖。


          三個(gè)主要的函數(shù)。

          # 創(chuàng)建一個(gè)隨機(jī)的x/y坐標(biāo)列表,并使用mask對(duì)其進(jìn)行過濾。
          get_masked_data()
          # 將文本轉(zhuǎn)化為數(shù)據(jù)
          text_to_data()
          # 使用坐標(biāo)點(diǎn)生成散點(diǎn)圖, 保存GIF
          build_gif()


          這里小F給大家提供了26個(gè)字母,大伙可以自行組合。

          當(dāng)然其他圖形也是可以的,就是需要自己作圖。

          圖片的大小應(yīng)為1000x1000像素,mask著色為黑色,背景為白色。

          然后png文件保存在images/letters文件夾中,單獨(dú)一個(gè)字符命名

          coordinates_lists = text_to_data('mac_', repeat=True, intensity=50)

          build_gif(coordinates_lists,
                    gif_name='mac',
                    n_frames=7,
                    bg_color='#F5B63F',
                    marker_color='#000000',
                    marker_size=0.2)


          結(jié)果如下,最后一張是個(gè)人物像。


          好了,本期的分享就到此結(jié)束了。

          使用Matplotlib+Imageio創(chuàng)建動(dòng)態(tài)圖表,案例比較簡(jiǎn)單,大家可以自行下載代碼進(jìn)行學(xué)習(xí)。


          最后在公眾號(hào)回復(fù)「動(dòng)畫」,獲取到本次使用到的代碼和數(shù)據(jù)


          點(diǎn)分享
          點(diǎn)收藏
          點(diǎn)點(diǎn)贊
          點(diǎn)在看
          瀏覽 74
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一呦二呦三呦 | 2020天天日天天射 | 免费黄色在线观看国产 | 成人性爱免费视频 | 大香交|