整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          詳解PostgreSQL Sequence序列

          詳解PostgreSQL Sequence序列

          文討論的內容是PostgreSQL中,高效生成序列化數字和ID的機制: Sequence,就是序列。

          什么是Sequence

          Sequence的英文意為“序列”,就是一個有序的、可以單調遞增的整數序列。

          在Postgres中, Sequence是一個序列數字的生成器。在使用前,需要先創建一個序列的實例,然后可以將這個實例作為數據類型,應用到數據庫表字段定義當中。當然,也可以直接使用序列相關的操作方法,如從序列實例中獲取下一個值或者當前值,來支持特定的業務應用需求。

          在數據庫應用中,序列技術最常用的場景,就是輔助生成一個數據庫級別的不重復的編號,可以作為一個標識。這一就可以解決了數據庫表中,記錄主鍵或者唯一標識的問題。

          如果沒有系統級內置的序列機制的話,在程序級別的實現是比較麻煩的。例如,需要查詢表中,最大的記錄ID,然后加1作為新值;或者隨機生成一個ID,再檢查表中是否有重復的信息;或者使用一個ID記錄表,批量生成后,使用并標記可用的ID;再有就是使用UUID的機制。這些實現,相對而言都比較麻煩,而且代價也比較高昂。

          Postgres中,如何使用序列

          Postgres官方技術文檔的序列章節的鏈接如下:

          https://www.postgresql.org/docs/16/sql-createsequence.html

          在Postgres中使用序列,一般涉及以下相關操作。

          創建序列實例

          以下的語句,可以在數據庫中,創建一個序列實例:

          CREATE SEQUENCE sq_common
            START 1
            INCREMENT 1
            MINVALUE 1
            MAXVALUE 200000000
            CACHE 10;
          CREATE SEQUENCE
          
          -- 簡單寫法
          CREATE SEQUENCE sq_common;
          
          -- 完整的語法
          CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] SEQUENCE [ IF NOT EXISTS ] name
              [ AS data_type ]
              [ INCREMENT [ BY ] increment ]
              [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
              [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
              [ OWNED BY { table_name.column_name | NONE } ]

          這里的要點如下:

          • 語句定義了一個名為 sq_common的序列實例
          • 可以定義起始值(默認1)、最小值、最大值、步進值(默認為1)
          • 簡單的寫法,只需要指定一個名稱
          • 緩存值和序列生成的緩存機制,可以幫助提高序列生成和使用的性能
          • 在Postgres中,本質上序列的值就是一個bigint整數,其限制就是bigint的取值范圍,當然用戶可以指定其范圍
          • 可以使用CYCLE指定序列是否可以循環取值(用完后從頭開始)

          數據庫字段

          使用以下語句和方法,創建一個使用序列的數據庫表:

          CREATE TABLE my_table (
            id INTEGER PRIMARY KEY DEFAULT NEXTVAL('sq_common'),
            name VARCHAR(50)
          );
          
          \d my_table
                                          Table "public.my_table"
           Column |         Type          | Collation | Nullable |            Default             
          --------+-----------------------+-----------+----------+--------------------------------
           id     | integer               |           | not null | nextval('sq_common'::regclass)
           name   | character varying(50) |           |          | 
          Indexes:
              "my_table_pkey" PRIMARY KEY, btree (id)

          從描述信息來看,和想象的不同,并不是說直接指定字段使用這個序列實例,而是先設置字段類型是interger,然后指定其默認值,來調用一個nextval()方法,方法的參數就是序列實例的名稱。這一,在插入新行的時候,這個字段如果沒有設置值,則會調用序列方法,獲取序列中下一個值,作為這個字段的值。

          注意這里nextval中的參數的完整定義,類型是regclass。也說就是,實際nextval的參數,是一個對象,regclass轉換可以使用名稱來映射這個對象,作為參數。

          在這個例子中,利用序列實例,定義了一個主鍵字段。使用同樣的道理,還可以利用序列定義其他的不重復編碼的值的字段。此外,由于這種使用方式,我們也可以在不同的表中,共享這個序列。

          序列值操作

          上面定義數據庫表字段的示例中,我們已經看到,可以使用相關的函數來獲得序列實例中的下一個值。關于這種類似的序列操作的函數包括:

          • nextval() 獲取序列實例的下一個序列值,而且當函數調用時,序列實例中的當前值會自動步進
          • currval() 獲取序列實例當前值,同時序列不會步進
          • setval() 給序列設置當前值,可選獲取下一個值或者當前值
          • lastval() 獲取當前session中,最后一個由序列生成的值(注意不需要指定實例)

          下面是相關代碼示例:

          -- 下一值
          defaultdb=> select nextval('sq_common');
           nextval 
          ---------
                 3
          (1 row)
          
          -- 當前值
          defaultdb=> select currval('sq_common');
           currval 
          ---------
                 3
          (1 row)
          
          -- 設置值
          defaultdb=> SELECT setval('sq_common', 42); 
           setval 
          --------
               42
          (1 row)
          
          defaultdb=> SELECT setval('sq_common', 42, false); 
           setval 
          --------
               42
          (1 row)
          
          
          -- 利用序列來編碼
           select 'CODE-' ||  LPAD('' || nextval('sq_common'),5,'0');
            ?column?  
          ------------
           CODE-00007
          (1 row)

          序列實例管理

          在序列實例創建之后,可以使用相關語句對其進行管理。

          • ALTER SEQUENCE 修改序列實例的起始、步進、最大值和最小值

          簡單示例如下:

          -- 修改起始值
          alter sequence sq_common  start 1001;
          ALTER SEQUENCE
          
          -- 刪除實例
          drop sequence sq_common;
          ERROR:  cannot drop sequence sq_common because other objects depend on it
          DETAIL:  default value for column id of table my_table depends on sequence sq_common
          HINT:  Use DROP ... CASCADE to drop the dependent objects too.

          這里修改了start值后,其實對序列當前值并沒有直接的影響。也許如果可以循環,它會以這個值作為起始吧。如果確實需要修改序列當前的值,需要使用setval方法。

          • DROP SEQUENCE 刪除序列實例

          要注意刪除的時候,對使用該實例的表的影響(上例)。例如,如果序列在被使用的時候,其實是不能直接刪除的。相關的錯誤信息可以清晰的指出操作失敗的原因和位置。

          如何查詢序列的狀態

          有趣的是,作為一個數據庫對象,可以直接使用select語句,來查詢序列實例的狀態。

          SELECT * FROM sq_common;
           last_value | log_cnt | is_called 
          ------------+---------+-----------
                    3 |      30 | t
          (1 row)

          在Postgres中,有一個serial數據類型,和序列是什么關系

          筆者理解,這實際上是一個語法糖,或者批處理標識。目的是簡化表的定義,因為這是一種比較常見的需求。

          具體操作過程應該是這樣的。系統如果發現定義字段的類型是serial,在創建表時,就會使用默認設置和命名規則,先創建一個序列實例,然后使用這個序列實例名稱,設置到字段默認值的定義當中。

          比如上面的例子,可以簡化為:

          defaultdb=> CREATE TABLE my_table2 (
            id SERIAL PRIMARY KEY,
            name VARCHAR(50)
          );
          CREATE TABLE
          
          defaultdb=> \d my_table2;
                                             Table "public.my_table2"
           Column |         Type          | Collation | Nullable |                Default                
          --------+-----------------------+-----------+----------+---------------------------------------
           id     | integer               |           | not null | nextval('my_table2_id_seq'::regclass)
           name   | character varying(50) |           |          | 
          Indexes:
              "my_table2_pkey" PRIMARY KEY, btree (id)

          這樣就簡單了很多,而且效果基本一樣。

          創建Sequence語句中,有兩個關鍵字Temp和Unlogged是做什么的

          Temp關鍵字,可以指定當前創建的序列是一個臨時序列,只對當前會話有效,有點像Temp Table的意思。

          Unlogged關鍵字,該設置將創建為不記錄的序列。也就是說,對序列所做的修改,不會寫到日志中。如果系統崩潰或者意外關閉,這個序列會重置到初始狀態。但意外的是,這個設置并沒有提供顯著的性能優勢(比如unlogged insert),主要用于配合非日志記錄的表來使用。實際應用中,應該沒有機會接觸到這種場景。

          使用序列需要注意什么問題

          筆者在日常使用序列,覺得有一些觀點和經驗可以分享一些:

          • SQL語句中,是可以直接使用序列函數的
          • 如果沒有其他的特別原因,可以考慮共享序列
          • 序列不僅僅可以用于標識字段,也可以通過配合字符串函數,作為不重復編碼的生成方式
          • 在做數據遷移的時候,要特別注意序列實例一般是無法遷移的,可能需要一些手動的處理和操作
          • 不同系統中的序列,特別是遷移的序列的值,要特別注意其一致性
          • 需要理解,序列對象實例,和數據庫字段的值沒有直接的邏輯聯系,只是借助了序列操作結果而已,所以特別要注意在修改序列狀態的時候,可能對字段值造成的取值沖突
          • 序列值是可以不連續的(修改步長不為1),雖然好像沒有特別的理由這么做
          • 作為系統級別的實現,應當優先使用序列作為不重復值生成機制

          小結

          在Postgres中,序列是一種系統級別的高效生成序列化數組的機制。本文討論了這個機制相關實現和操作的過程,相關數據類型和數據字段的定義,相關函數的使用方法和場景,以及需要注意的問題等內容。


          作者:JohnYan

          鏈接:https://juejin.cn/post/7389579792510943295

          #記錄我的2024#

          列類型的分類

          按照序列存儲數據的類型

          • 容器序列:list, tuple, deque
          • 扁平序列:str, bytes, array.array, bytearray

          按照序列是否可變

          • 可變序列:list, deque, array.array, bytearray
          • 不可變序列:str, tuple, bytes

          序列的abc繼承關系

          引言

          • collections.abc模塊中有很多內置的抽象基類
          • 序列主要和兩個抽象基類有關:Sequence 和 MutableSequence

          細節

          Sequence

          """
          Sequence 抽象基類
          它繼承了Sized Iterable Contain 這三個抽象基類
          """
          class Sequence(Sized, Iterable, Container):
           """All the operations on a read-only sequence.
           Concrete subclasses must override __new__ or __init__,
           __getitem__, and __len__.
           """
           __slots__=()
           
           @abstractmethod
           def __getitem__(self, index):
           raise IndexError
           
           def __iter__(self):
           i=0
           try:
           while True:
           v=self[i]
           yield v
           i +=1
           except IndexError:
           return
           
           def __contains__(self, value):
           for v in self:
           if v==value:
           return True
           return False
           
           def __reversed__(self): # 該魔法函數使得序列類型可以反轉
           for i in reversed(range(len(self))):
           yield self[i]
          
          class Sized(metaclass=ABCMeta):
           __slots__=()
           
           @abstractmethod
           def __len__(self): # 這個魔法函數使得序列類型可以使用 len()獲得長度
           return 0
           
           @classmethod
           def __subclasshook__(cls, C):
           if cls is Sized:
           if any("__len__" in B.__dict__ for B in C.__mro__):
           return True
           return NotImplemented
          
          class Iterable(metaclass=ABCMeta):
           __slots__=()
           
           @abstractmethod
           def __iter__(self): # 這個魔法函數使得序列類型可以進行 for 循環
           while False:
           yield None
           
           @classmethod
           def __subclasshook__(cls, C):
           if cls is Iterable:
           if any("__iter__" in B.__dict__ for B in C.__mro__):
           return True
           return NotImplemented
          
          class Container(metaclass=ABCMeta):
           __slots__=()
           
           @abstractmethod
           def __contains__(self, x): # 這個魔法函數使得序列類型可以使用 in 操作符
           return False
           
           @classmethod
           def __subclasshook__(cls, C):
           if cls is Container:
           if any("__contains__" in B.__dict__ for B in C.__mro__):
           return True
           return NotImplemented
          

          MutableSequence

          class MutableSequence(Sequence):
           __slots__=()
           """All the operations on a read-write sequence.
           Concrete subclasses must provide __new__ or __init__,
           __getitem__, __setitem__, __delitem__, __len__, and insert().
           """
           @abstractmethod
           def __setitem__(self, index, value):
           raise IndexError
           
           @abstractmethod
           def __delitem__(self, index):
           raise IndexError
           
           def __iadd__(self, values): # 使得序列可以使用 +=運算符號進行計算
           self.extend(values)
           return self
           
           # ......只列出幾個有代表意義的魔法函數......
           # 這些魔法函數使得序列類型可以改變
          

          序列的+、+=和extend

          引言

          • 對于可變序列,我們有三種方式來擴充原始序列
          • 三種方式有原理效率上的不同,我們要根據實際情況選擇合適的方式來使用

          使用范例

          """
          普通的加法會在內存中產生一個新的序列對象
          時間 空間開銷相對比較大
          必須要是類型完全一致才能夠使用這種合并方式
          """
          a, b, c=[1, 2], [3, 4], (5, 6)
          d=a + b # [1, 2] + [3, 4]
          print(d)
          # result:
          # [1, 2, 3, 4]
          d=a + c # [1, 2] + (5, 6)
          print(d)
          # result:
          # TypeError: can only concatenate list (not "tuple") to list
          
          """
          +=可以理解為原地加,單純地在原對象上進行修改,效率比 + 要高很多
          這個功能背后有一個魔法函數,__iadd__(self, values)
          上一小節中有這個函數具體的實現,我們可以看到,它是調用了 extend()方法
          所以本質上,它和調用 extend()函數產生的效果是一模一樣的
          """
          a, b, c=[1, 2], [3, 4], (5, 6)
          a +=b
          print(a)
          # result:
          # [1, 2, 3, 4]
          a +=c
          print(a)
          # result:
          # [1, 2, 3, 4, 5, 6]
          

          鴨子類型那一小節,我們分析了extend()函數的參數要求,只要是可迭代類型都能夠作為參數擴充列表。

          +=操作本質上就是調用 extend() 函數,所以能將列表元組進行合并也就不奇怪了,因為他們本質上都是可迭代的類型。

          實現可切片的對象

          引言

          切片介紹

          # 使用模式:[start:end:step]
          """
          start: 切片開始的位置,默認是 0
          end: 切片截止(不包括)的位置,默認是列表長度
          step: 切片的步長,默認是 1
          start和end為默認值時可以省略不寫,step為默認值時可以連同最后的冒號一起省略
          step為負數時,表示反向切片,此時需滿足: start > end
          """
          

          切片舉例

          li=[8, 4, 3, 2, 1, 7]
          li[::] # 返回原序列
          li[::-1] # 返回原序列的逆序列表
          li[::2] # 隔一個取一個,所有偶數位置
          li[1::2] # 隔一個取一個,所有奇數位置
          li[3:6] # 取[3, 6)區間內的所有元素,左包含,右不包含
          li[0:666] # 結束位置大于列表長度,從尾部截斷,返回原列表
          li[666:] # 開始位置大于列表長度,直接返回空列表
          li=[6, 6]
          li[:0]=[1, 2] # 列表頭部插入元素 [1, 2, 6, 6]
          li[1:1]=[1, 1] # 列表的某一位置插入元素 [6, 1, 1, 6]
          li[:1]=[1, 1] # 替換列表元素 [1, 1, 6]
          li[1:]=[1, 1] # 替換列表元素 [6, 1, 1]
          

          使用案例

          class Group:
           def __init__(self, group_name, staffs):
           self.group_name=group_name
           self.staffs=staffs
           
           def __reversed__(self): # reversed(group)
           self.staffs.reverse()
           
           """
           在使用切片和索引訪問操作時,相關的參數會傳遞到 __getitem__(self, item) 中
           我們通常希望,切片返回的對象和原對象是同一種類型
           使用切片的話,傳入的item是一個slice類型
           使用索引訪問,傳入的item是一個int類型
           """
           def __getitem__(self, item): # 對象可切片的關鍵
           cls=type(self) # 獲得該實例的類型
           if isinstance(item, slice):
           return cls(self.group_name, self.staffs[item]) # 委托給列表實現切片
           elif isinstance(item, int):
           return self.staffs[item]
           
           def __len__(self):
           return len(self.staffs)
           
           def __iter__(self):
           return iter(self.staffs) # 后面會詳細講解
           
           def __contains__(self, item):
           return item in self.staffs # 同樣委托給列表的in操作來實現
           
           def __str__(self):
           return "name:{name}\nstaffs:{staffs}".format(name=self.group_name, staffs=self.staffs)
          staffs=["MetaTian0", "MetaTian1", "MetaTian2"]
          group=Group("HIT", staffs)
          print(group[1]) # 索引訪問
          print(group[0:2]) # 切片
          reversed(group) # 反轉
          print(group)
          print(len(group)) # 長度
          print("MetaTian0" in group, "MetaTian6" in group) # in 操作符
          # resutl:
          # MetaTian1
          # name:HIT; staffs:['MetaTian0', 'MetaTian1']
          # name:HIT; staffs:['MetaTian2', 'MetaTian1', 'MetaTian0']
          # 3
          # True False
          

          bisect模塊

          引言

          • 如果我們在一個有序序列中需要增加一個元素,但任然要維持序列的有序性,可以使用append()添加元素,再調用sort()來重新排序。
          • bisect模塊用來維持已排序序列(升序)的順序,效率更高。
          • 兩個關鍵函數insort()和bisect

          使用案例

          import bisect
          int_list=[]
          """
          在插入過程中維護序列的有序性
          """
          bisect.insort(int_list, 2)
          bisect.insort(int_list, 6)
          bisect.insort(int_list, 1)
          bisect.insort(int_list, 3)
          bisect.insort(int_list, 5)
          print(int_list)
          # result:
          # [1, 2, 3, 5, 6]
          """
          查詢一個元素在序列中應該插入的位置
          bisect(): 同值元素默認插在右側
          bisect_left(): 同值元素默認插在左側
          """
          print(bisect.bisect(int_list, 3))
          print(bisect.bisect_left(int_list, 3))
          # result:
          # 3
          # 2
          

          列表一定是最好的嗎

          引言

          • Python中的array,只可以存儲指定類型的元素
          • 相當與是C語言中的數組,操作效率比列表更高
          • 官方文檔:https://docs.python.org/3.7/library/array.html?highlight=array

          使用案例

          import array
          """
          使用前要引入array模塊,要申明存儲的對象類型
          """
          my_array=array.array("i")
          my_array.append(666)
          my_array.append(4)
          my_array.extend([1, 2, 3])
          my_list=my_array.tolist()
          # 大部分基本的操作函數和序列差不多,這里就不做過多介紹
          print(my_array)
          # result:
          # array('i', [666, 4, 1, 2, 3])
          # [666, 4, 1, 2, 3]
          

          列表推導、生成器表達式、字典推導

          引言

          • 推導表達式是一種更加簡潔高效,生成相關對象的一種方式

          使用案例

          """
          列表推導
          生成1-20中的奇數
          """
          # 普通方式
          odd_list=[]
          for i in range(1, 21):
           if i%2==1:
           odd_list.append(i)
          # 列表推導
          odd_list=[i for i in range(1, 21) if i%2==1]
          """
          生成器表達式
          后面部分會詳細介紹其原理
          """
          odd_list=(i for i in range(1, 21) if i%2==1)
          print(type(odd_list))
          # result:
          # <class 'generator'>
          for item in odd_list:
           print(item, end=' ')
           
          # result:
          # 1 3 5 7 9 11 13 15 17 19 
          """
          字典推導式
          """
          my_dict={"a":1, "b":2, "c":3}
          reversed_dict={val:k for k, val in my_dict.items()}
          print(reversed_dict)
          # result:
          # {1: 'a', 2: 'b', 3: 'c'}
          

          喜歡python + qun:839383765 可以獲取Python各類免費最新入門學習資料!

          讀:什么是時間序列?用來展示什么樣的數據關系?怎樣用Python實現?本文將為你解答。

          作者:屈希峰,資深Python工程師,知乎多個專欄作者

          來源:華章科技

          01 概述

          時間序列(Time series)是指將某種現象某一個統計指標在不同時間上的各個數值,按時間先后順序排列而形成的序列。時間序列法是一種定量預測方法,也稱簡單外延法,在統計學中作為一種常用的預測手段被廣泛應用。

          時間序列分析在第二次世界大戰前應用于經濟預測?!岸稹敝泻汀岸稹焙?,在軍事科學、空間科學、氣象預報和工業自動化等領域的應用更加廣泛。

          時間序列分析(Time Series Analysis)是一種動態數據處理的統計方法。該方法基于隨機過程理論和數理統計學方法,研究隨機數據序列所遵從的統計規律,用于解決實際問題。時間序列構成要素是現象所屬的時間和反映現象發展水平的指標數值,如下圖所示。

          ▲時間序列

          時間序列中的每個觀察值大小,是影響變化的各種不同因素在同一時刻發生作用的綜合結果。從這些影響因素發生作用的大小和方向變化的時間特性來看,這些因素造成的時間序列數據的變動分為如下4種類型。

          1. 趨勢性:某個變量隨著時間進展或自變量變化,呈現出一種比較緩慢而長期的持續上升、下降、停留的同性質變動趨向,但變動幅度可能不相等。
          2. 周期性:某因素由于外部影響隨著自然季節的交替出現高峰與低谷的規律。
          3. 隨機性:個別為隨機變動,整體呈統計規律。
          4. 綜合性:實際變化情況是幾種變動的疊加或組合。預測時設法過濾除去不規則變動,突出反映趨勢性和周期性變動。

          02 實例

          時間序列代碼示例如下所示。

          • 代碼示例①
           1from?bokeh.sampledata.stocks?import?AAPL??
           2import?numpy?as?np??
           3#?數據??
           4aapl?=?np.array(AAPL['adj_close'])??
           5aapl_dates?=?np.array(AAPL['date'],?dtype=np.datetime64)??
           6window_size?=?30??
           7window?=?np.ones(window_size)/float(window_size)??
           8aapl_avg?=?np.convolve(aapl,?window,?'same')??
           9#?畫布??
          10p?=?figure(width=800,?height=350,?x_axis_type="datetime")??
          11#?圖層??
          12p.circle(aapl_dates,?aapl,?size=4,?color='darkgrey',?alpha=0.2,?legend='close')??
          13p.line(aapl_dates,?aapl_avg,?color='red',?legend='avg')??
          14#?自定義屬性??
          15p.title.text?=?"AAPL?One-Month?Average"??
          16p.legend.location?=?"top_left"??
          17p.grid.grid_line_alpha=0??
          18p.xaxis.axis_label?=?'Date'??
          19p.yaxis.axis_label?=?'Price'??
          20p.ygrid.band_fill_color="gray"??
          21p.ygrid.band_fill_alpha?=?0.1??
          22p.legend.click_policy="hide"?#?點擊圖例顯示隱藏數據??
          23#?顯示結果??
          24show(p)??

          運行結果如圖1所示。

          ▲圖1 代碼示例①運行結果

          代碼示例①第8行np.convolve用來計算離散點的移動平均值;第10行在畫布中預定義x軸的數據類型為datetime;第12行繪制離散的點(散點圖);第13行繪制曲線。第15~22行是關于圖例、坐標軸的一些自定義屬性,將在后文進行詳述。

          • 代碼示例②
           1import?numpy?as?np??
           2from?bokeh.models?import?ColumnDataSource,?CustomJSTransform??
           3from?bokeh.plotting?import?figure??
           4from?bokeh.io?import?output_file,?show??
           5from?bokeh.sampledata.stocks?import?AAPL,?GOOG??
           6from?bokeh.transform?import?transform??
           7#?數據轉換為時間類型??
           8def?datetime(x):??
           9??????return?np.array(x,?dtype=np.datetime64)??
          10#?畫布??
          11plot?=?figure(x_axis_type="datetime",?title="Normalized?Stock?Closing?Prices",
          12??????????????????????????plot_width=800,?plot_height=350)??
          13#?其他??
          14plot.background_fill_color?=?"#f0f0f0"??
          15plot.xgrid.grid_line_color?=?None??
          16plot.ygrid.grid_line_color?=?"black"??
          17plot.ygrid.grid_line_alpha?=?0.1??
          18plot.xaxis.axis_label?=?'Date'??
          19plot.yaxis.axis_label?=?'Normalized?Price'??
          20#?數據??
          21aapl_source?=?ColumnDataSource(data=dict(??
          22????????aapl_date=datetime(AAPL['date']),??
          23????????aapl_close=AAPL['adj_close'],??
          24))??
          25
          26goog_source?=?ColumnDataSource(data=dict(??
          27????????goog_date=datetime(GOOG['date']),??
          28????????goog_close=GOOG['adj_close'],??
          29))??
          30#?CustomJSTransform??
          31v_func?=?"""?
          32???????const?first?=?xs[0]?
          33???????const?norm?=?new?Float64Array(xs.length)?
          34???????for?(let?i?=?0;?i?<?xs.length;?i++)?{?
          35?????????????norm[i]?=?xs[i]?/?first?
          36???????}?
          37???????return?norm?
          38"""??
          39normalize?=?CustomJSTransform(v_func=v_func)??
          40#?繪圖??
          41plot.line(x='aapl_date',?y=transform('aapl_close',?normalize),?line_width=2,??
          42??????????????color='#cf3c4d',?alpha=0.6,legend="Apple",?source=aapl_source)??
          43plot.line(x='goog_date',?y=transform('goog_close',?normalize),?line_width=2,??
          44????????????color='#2f7bce',?alpha=0.6,?legend="Google",?source=goog_source)??
          45plot.legend.location='top_left'??
          46#?顯示??
          47show(plot)??

          運行結果如圖3所示。

          ▲圖3 代碼示例②運行結果

          代碼示例②第11行在畫布中預定義x軸的數據類型為datetime;第41、43行繪制兩條時間序列曲線。第31行采用JavaScript函數對y軸數據進行標準化處理,如果對JavaScript函數不熟悉,可以在Pandas中對原始數據進行預處理,然后直接進行調用。

          • 代碼示例③
           1from?bokeh.models?import?BoxAnnotation??
           2from?bokeh.sampledata.glucose?import?data?as?data_or??
           3import?numpy?as?np????
           4#?工具條??
           5TOOLS?=?"pan,wheel_zoom,box_zoom,reset,save"??
           6#?數據??
           7data?=?data_or.sort_index()['2010-03-24':'2010-03-25']??
           8#?畫布??
           9p?=?figure(x_axis_type="datetime",?tools=TOOLS,?title="Glocose?Readings,?Oct?4th?(Red?=?Outside?Range)")??
          1010.???
          11#?繪圖??
          12p.line(np.array(data.index.tolist(),?dtype=np.datetime64),?data.glucose.values,?line_color='gray')??
          13p.circle(data.index,?data.glucose,?color='grey',?size=1)??
          14#?箱形標記??
          15p.add_layout(BoxAnnotation(top=80,?fill_alpha=0.1,?fill_color='red',?line_color='red'))??
          16p.add_layout(BoxAnnotation(bottom=180,?fill_alpha=0.1,?fill_color='red',?line_color='red'))??
          17#?其他??
          18p.background_fill_color?=?"#efefef"??
          19p.xgrid.grid_line_color=None??
          20p.xaxis.axis_label?=?'Time'??
          21p.yaxis.axis_label?=?'Value'??
          22show(p)??

          運行結果如圖3所示。

          ▲圖3 代碼示例③運行結果

          代碼示例③在時間序列曲線的基礎上增加了箱形標記,深色區域為需要突出顯示的數據,讀者僅需要知道這種標記展示方式,后文會詳述箱形標記方法。

          • 代碼示例④
           1import?numpy?as?np??
           2from?bokeh.layouts?import?gridplot??
           3from?bokeh.sampledata.stocks?import?AAPL,?GOOG,?IBM,?MSFT??
           4def?datetime(x):??
           5????return?np.array(x,?dtype=np.datetime64)??
           6#?畫布1??
           7p1?=?figure(x_axis_type="datetime",?title="Stock?Closing?Prices")??
           8p1.grid.grid_line_alpha=0.3??
           9p1.xaxis.axis_label?=?'Date'??
          10p1.yaxis.axis_label?=?'Price'??
          11#?繪圖1??
          12p1.line(datetime(AAPL['date']),?AAPL['adj_close'],?color='#A6CEE3',?legend='AAPL')
          13p1.line(datetime(GOOG['date']),?GOOG['adj_close'],?color='#B2DF8A',?legend='GOOG')
          14p1.line(datetime(IBM['date']),?IBM['adj_close'],?color='#33A02C',?legend='IBM')
          15p1.line(datetime(MSFT['date']),?MSFT['adj_close'],?color='#FB9A99',?legend='MSFT')
          16p1.legend.location?=?"top_left"??
          17#?數據2??
          18aapl?=?np.array(AAPL['adj_close'])??
          19aapl_dates?=?np.array(AAPL['date'],?dtype=np.datetime64)??
          20window_size?=?30??
          21window?=?np.ones(window_size)/float(window_size)??
          22aapl_avg?=?np.convolve(aapl,?window,?'same')??
          23#?畫布2??
          24p2?=?figure(x_axis_type="datetime",?title="AAPL?One-Month?Average")??
          25p2.grid.grid_line_alpha?=?0??
          26p2.xaxis.axis_label?=?'Date'??
          27p2.yaxis.axis_label?=?'Price'??
          28p2.ygrid.band_fill_color?=?"olive"??
          29p2.ygrid.band_fill_alpha?=?0.1??
          30p2.circle(aapl_dates,?aapl,?size=4,?legend='close',??
          31???????????????????color='darkgrey',?alpha=0.2)??
          32p2.line(aapl_dates,?aapl_avg,?legend='avg',?color='navy')??
          33p2.legend.location?=?"top_left"??
          34#?顯示??
          35show(gridplot([[p1,p2]],plot_width=400,?plot_height=400))

          運行結果如圖4所示。

          ▲圖4 代碼示例④運行結果

          代碼示例④采用網格布局顯示兩張時間序列曲線,可以對某一曲線進行橫向比較。

          • 代碼示例⑤
           1from?numpy?import?pi,?exp,?linspace,?sin??
           2import?time??
           3from?bokeh.util.browser?import?view??
           4from?bokeh.document?import?Document??
           5from?bokeh.embed?import?file_html??
           6from?bokeh.models.glyphs?import?Circle??
           7from?bokeh.models?import?Plot,?DatetimeAxis,?ColumnDataSource,?PanTool,?WheelZoomTool??
           8from?bokeh.resources?import?INLINE??
           9#?數據??
          10N?=?200??
          11x?=?linspace(-2?*?pi,?2?*?pi,?N)??
          12y?=?sin(x)*exp(-x)??
          13#?創建一組時間數據,以當前時間往后延伸24小時??
          14times?=?(linspace(0,?24*3600,?N)?+?time.time())?*?1000??
          15source?=?ColumnDataSource(data=dict(x=x,?y=y,?times=times))??
          16#?畫布??
          17plot?=?Plot(min_border=80,?plot_width=800,?plot_height=350,?background_fill_color="#efefef")??
          18#?繪圖??
          19circle?=?Circle(x="times",?y="y",?fill_color="red",?size=3,?line_color=None,?fill_alpha=0.5)??
          20plot.add_glyph(source,?circle)??
          21#?設置時間軸??
          22plot.add_layout(DatetimeAxis(),?'below')??
          23plot.add_layout(DatetimeAxis(),?'left')??
          24#?設置工具條??
          25plot.add_tools(PanTool(),?WheelZoomTool(zoom_on_axis=False,?speed=1/5000.))??
          26#?顯示??
          27show(plot)??

          運行結果如圖5所示。

          ▲圖5 代碼示例⑤運行結果

          代碼示例⑤采用modes接口進行圖形繪制,第25行為該圖形增加平移工具并自定義滾輪縮放的速率。讀者僅需要了解采用這種方式進行繪圖的基本流程即可。

          關于作者:屈希峰,資深Python工程師,Bokeh領域的實踐者和布道者,對Bokeh有深入的研究。擅長Flask、MongoDB、Sklearn等技術,實踐經驗豐富。知乎多個專欄(Python中文社區、Python程序員、大數據分析挖掘)作者,專欄累計關注用戶十余萬人。

          本文摘編自《Python數據可視化:基于Bokeh的可視化繪圖》,經出版方授權發布。

          延伸閱讀《Python數據可視化》

          推薦語:從圖形繪制、數據動態展示、Web交互等維度全面講解Bokeh功能和使用,不含復雜數據處理和算法,深入淺出,適合零基礎入門,包含大量案例。


          主站蜘蛛池模板: 国产成人av一区二区三区在线观看 | 一本一道波多野结衣一区| 中文字幕乱码亚洲精品一区| 国产在线精品一区二区不卡麻豆| 一区二区亚洲精品精华液| 中文字幕永久一区二区三区在线观看 | 一区二区三区在线免费| 91精品一区二区综合在线| 国模少妇一区二区三区| 国模私拍一区二区三区| 乱色精品无码一区二区国产盗| 无码人妻少妇色欲AV一区二区| 亚洲AV本道一区二区三区四区| 麻豆一区二区99久久久久| 无码少妇一区二区性色AV| 亚洲码一区二区三区| 国产精品福利一区二区久久| 国产美女一区二区三区| 久久er99热精品一区二区| 精品无码人妻一区二区三区18| 精品少妇人妻AV一区二区三区| 另类国产精品一区二区| 国产午夜精品一区二区| 91午夜精品亚洲一区二区三区 | 伊人色综合视频一区二区三区| 无码人妻精品一区二区三区久久久| 波多野结衣一区二区三区高清在线| 国产激情一区二区三区小说| 亚洲日韩激情无码一区| 欧美激情一区二区三区成人| 亚洲色精品vr一区二区三区 | 久久99国产精品一区二区| 亚洲av乱码一区二区三区| 免费无码一区二区三区蜜桃| 国产午夜精品片一区二区三区| 国产亚洲无线码一区二区| 福利一区二区在线| 无码丰满熟妇浪潮一区二区AV| 精品一区二区在线观看| 东京热无码一区二区三区av| 国产一区视频在线免费观看|