1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        讓數(shù)據(jù)動(dòng)起來!用Python制作動(dòng)畫可視化效果,讓數(shù)據(jù)不再枯燥!

        共 28014字,需瀏覽 57分鐘

         ·

        2021-06-19 17:28


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


        今天小F就給大家介紹一下,如何用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í)。


        ···  END  ···
        推薦閱讀:
        一、Number(數(shù)字)
        Python基礎(chǔ)之?dāng)?shù)字(Number)超級(jí)詳解
        Python隨機(jī)模塊22個(gè)函數(shù)詳解
        Python數(shù)學(xué)math模塊55個(gè)函數(shù)詳解
        二、String(字符串)
        Python字符串的45個(gè)方法詳解
        Pandas向量化字符串操作
        三、List(列表)
        超級(jí)詳解系列-Python列表全面解析
        Python輕量級(jí)循環(huán)-列表推導(dǎo)式
        四、Tuple(元組)
        Python的元組,沒想象的那么簡(jiǎn)單
        五、Set(集合)
        全面理解Python集合,17個(gè)方法全解,看完就夠了
        六、Dictionary(字典)
        Python字典詳解-超級(jí)完整版
        七、內(nèi)置函數(shù)
        Python初學(xué)者必須吃透這69個(gè)內(nèi)置函數(shù)!
        八、正則模塊
        Python正則表達(dá)式入門到入魔
        筆記 | 史上最全的正則表達(dá)式
        八、系統(tǒng)操作
        Python之shutil模塊11個(gè)常用函數(shù)詳解
        Python之OS模塊39個(gè)常用函數(shù)詳解
        九、進(jìn)階模塊
        【萬字長(zhǎng)文詳解】Python庫(kù)collections,讓你擊敗99%的Pythoner
        高手如何在Python中使用collections模塊

        掃描關(guān)注本號(hào)↓

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲永久免费 | 少妇的诱惑k8 | 日韩欧美三级在线 | 无码中文字幕一区二区免费蜜桃 | 操B视频在线观看 | 国产日韩欧美第一页 | 久久久久99精品成人网站 | 麻豆av在线免费观看 | 国产高清黄色片 | 國产精品少妇AⅤ免费 |