Pandas

在計算機編程中，pandas是用於數據操縱和分析的Python軟件庫。它建造在NumPy基礎上，併為操縱數值表格和時間序列，提供了數據結構和運算操作。它是在三條款BSD許可證下發行的自由軟件。它的名字衍生自術語“面板數據”（panel data），這是計量經濟學的術語，即包括了對同一個體在某個時期內多個時間點的觀測的數據集。它的名字還可解釋為對短語“Python data analysis”的玩笑。

**歷史**
2008年，原作者Wes McKinney開始在AQR資本管理公司製作pandas，用來滿足在財務數據上進行定量分析時，對高性能、靈活工具的需要。2009年，他在離開AQR之前，說服管理者允許他將這個軟件庫開放源代碼。下面是其開發過程的時間線：
*2008年，pandas開發開始。
*2009年，pandas開源。
*2012年，另一個AQR僱員Chang She加入了這個項目，併成為這個軟件庫的第二個主要貢獻者。第一版《Python for Data Analysis》發布。
*2015年，pandas簽約為NumFOCUS的一個財務贊助項目，NumFOCUS是美國的501(c)(3)非營利公益組織。
*2018年，舉行了第一次面對面的“核心開發者衝刺”。
*2022年，第三版《Python for Data Analysis》公開版在線發行。

**數據模型**
pandas的序列（Series）是一維的加標籤數據結構，它能夠持有任何數據類型，如整數、字符串、浮點數和Python對象等，軸標籤在集體上稱為索引（index）。序列表現得非常類似於NumPy的ndarray數據結構，並且是大多數NumPy函數的有效實際參數。
pandas提供了類似於R語言中data.frame對象的數據幀（DataFrame），它是二維的加標籤數據結構，其諸縱列潛在的可能具有不同的類型；數據幀就像是電子表格或SQL表，或者是序列的字典，這種格局也叫做數組之結構（SoA）。pandas允許各種數據操縱運算操作，比如選擇、歸併和重製形狀，還有數據清洗和data wrangling特徵。

**主要特徵**
pandas提供了快速而高效的數據幀對象，用於憑藉其集成的索引進行數據操縱。它的主要特徵有：
*易於將在其他的Python和NumPy數據結構中，參差不齊或不同索引的數據，轉換成數據幀對象。
*大小可變性，可以在數據幀和更高維對象中插入或刪除縱列。
*自動和顯式的“數據對齊”，標籤和數據之間的聯繫是固有的，但是可以顯式的控制二元運算的匹配和廣播行為。兩個序列對象按標籤自動對齊，兩個數據幀對象自動對齊於縱列標籤和索引（即橫行標籤）二者上，二元運算的結果對象具有雙方的縱列標籤和橫行標籤的並集；數據幀與序列對象之間的默認行為，是序列的索引自動對齊於數據幀的縱列標籤，從而縱向逐橫行廣播。
*易於處理缺失數據，它被表示為用於浮點數的NaN（即NumPy的nan）、用於日期時間的NaT或跨數據類型的NA。
*智能的對大數據集的基於標籤的分片，多重索引和其他花樣索引，依據布爾值向量的Subsetting。
*直觀的數據集的歸併和連接。
*強大而靈活的分組（groupby）功能，用來在數據集上進行分離-應用-合併（split-apply-combine）運算，它可用於數據聚合和變換二者。
*靈活的數據集的重製形狀（reshape）和樞軸彙總。
*軸可以有層級標籤，從而在繪圖時每個刻度可能有多重標籤。
*健壯的I/O工具，用於從CSV文件和其他平面文件、JSON文件、Parquet文件、Arrow的Feather文件、SQL表和查詢、Excel文件裝載數據，並以極快的HDF5格式保存/裝載數據。
*特定於時間序列的功能，例如日期範圍生成和頻率轉換，移動窗口統計比如移動平均，日期移位和滯後。
pandas經過了高度的性能優化，關鍵代碼路徑用Cython或C語言寫成。pandas可以利用PyArrow來擴展功能並增進各種API的性能。pandas的缺省繪圖後端是matplotlib，還可以擴展上其他第三方Plot (graphics)後端，比如Plotly Express。
SQL OLAP列式數據庫DuckDB，可以在pandas數據幀上執行SQL。

**示例**

**基本運算**
在下面的梗概示例中，展示針對數據幀的縱列和橫行的基本運算：
>>> import pandas as pd
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> 
>>> data = {
...     'place': 'A'*3 + 'B'*3 + 'C'*3 + 'D'*3,
...     'date': pd.to_datetime('2023-01-01', '2023-01-02', '2023-01-03' * 4),
...     'value': x + 0.1 for x in range(12)
... }
>>> 
>>> df = pd.DataFrame(data)
>>> type(df'value') == pd.Series
True
>>>
>>> df'value1' = df'value' + 0.1
>>> df
place       date  value  value1
0      A 2023-01-01    0.1     0.2
1      A 2023-01-02    1.1     1.2
2      A 2023-01-03    2.1     2.2
3      B 2023-01-01    3.1     3.2
4      B 2023-01-02    4.1     4.2
5      B 2023-01-03    5.1     5.2
6      C 2023-01-01    6.1     6.2
7      C 2023-01-02    7.1     7.2
8      C 2023-01-03    8.1     8.2
9      D 2023-01-01    9.1     9.2
10     D 2023-01-02   10.1    10.2
11     D 2023-01-03   11.1    11.2
>>> 
>>> df.index
RangeIndex(start=0, stop=12, step=1)
>>> 
>>> df.columns
Index('place', 'date', 'value', 'value1', dtype='object')
>>> 
>>> df.loc1, 2, 'value', 'value1' 
value  value1
1    1.1     1.2
2    2.1     2.2
>>> 
>>> df.at1, 'value'
1.1
>>> 
>>> df'value'.loc1
1.1
>>> 
>>> df'value','value1'.to_numpy().mean().round(2)
np.float64(5.65)
>>> df'value','value1'.to_numpy().mean(axis=0)
array(5.6, 5.7)
>>> df'value','value1'.to_numpy().mean(axis=1)
array( 0.15,  1.15,  2.15,  3.15,  4.15,  5.15,  6.15,  7.15,  8.15,
9.15, 10.15, 11.15)
>>> 
>>> df(df'value'/2 > 1) & (df'value1' >> 
>>> df.query('value/2 > 1 & value1 >>
>>> dfdf'value' == 1.1'value1'.round(2).item()
1.2
>>>

**數據幀格式**
數據幀中的數據經常存儲為兩種格式：堆疊（stack）格式或記錄（record）格式。在堆疊格式中，針對每個主題（subject）在適用情況下有多個橫行，故而也稱為“長”格式。在記錄格式中，針對每個主題典型地有一個橫行，故而也稱為“寬”格式。在這個例子中，如果要對縱列'place'標識出的每個唯一的變量（'A', 'B', 'C', 'D'）進行時間序列運算，更好的表示形式為：諸縱列都對應唯一的變量，即對應不同的觀測地點或觀測者，而日期索引（'date'）標識出每個（不可細分的）個體觀測。為此使用pivot()，將數據幀從堆疊格式重製形狀為記錄格式：
>>> df.drop(0, 4, 8).pivot(index='date', columns='place')
value                 value1                
place          A    B    C     D      A    B    C     D
date                                                   
2023-01-01   NaN  3.1  6.1   9.1    NaN  3.2  6.2   9.2
2023-01-02   1.1  NaN  7.1  10.1    1.2  NaN  7.2  10.2
2023-01-03   2.1  5.1  NaN  11.1    2.2  5.2  NaN  11.2
>>> 
這裡刪除了三個橫行展示了NaN被用來表示缺失數據。這裡在pivot()的輸入數據幀的諸縱列中，通過index參數指定了用作索引的縱列（'date'），通過columns參數指定了用作變量的縱列（'place'），但沒有通過指定values參數於多個值縱列（'value', 'value1'）裡選取其中之一，故而結果數據幀的諸縱列被納入層級式索引（即多重索引MultiIndex），其最頂層指示出各自的值縱列，即依據觀測量的不同而進行頂層分組。

**串接運算**
使用concat()對數據幀進行串接運算：
>>> df1 = df.drop(columns='value').rename(columns={'value1': 'value'})
>>> df1 = pd.concat(df.drop(columns='value1'), df1, ignore_index=True)
>>> df1.shape
(24, 3)
>>> 
>>> data1 = 
...     ('A', pd.to_datetime('2023-01-01'), 0.3),
...     ('A', pd.to_datetime('2023-01-02'), 1.3)
... 
>>> 
>>> new_rows = pd.DataFrame(data1, columns='place', 'date', 'value')
>>> pd.concat(df1, new_rows, ignore_index=True).tail(3)
place       date  value
23     D 2023-01-03   11.2
24     A 2023-01-01    0.3
25     A 2023-01-02    1.3
>>> 
>>> df_A = df1df1'place'=='A'.drop(columns='place')
>>> df_A
date  value
0  2023-01-01    0.1
1  2023-01-02    1.1
2  2023-01-03    2.1
12 2023-01-01    0.2
13 2023-01-02    1.2
14 2023-01-03    2.2
>>> 
>>> df_B = df1df1'place'=='B'.drop(columns='place')
>>> df_C = df1df1'place'=='C'.drop(columns='place')
>>> df_D = df1df1'place'=='D'.drop(columns='place')
>>> df1 = pd.concat(df_A, df_B, df_C, df_D, keys='A', 'B', 'C', 'D') \
...     .droplevel(1).rename_axis('place').reset_index()
>>> 

**分組聚合運算**
使用groupby()和緊隨其後的agg()，對數據幀進行分組並聚合運算：
>>> df2 = df1.groupby('date', 'place').agg({'value': 'sum'})
>>> df2
value
date       place       
2023-01-01 A        0.3
B        6.3
C       12.3
D       18.3
2023-01-02 A        2.3
B        8.3
C       14.3
D       20.3
2023-01-03 A        4.3
B       10.3
C       16.3
D       22.3
>>> 
>>> df2.reorder_levels('place', 'date').sort_index()
value
place date             
A     2023-01-01    0.3
2023-01-02    2.3
2023-01-03    4.3
B     2023-01-01    6.3
2023-01-02    8.3
2023-01-03   10.3
C     2023-01-01   12.3
2023-01-02   14.3
2023-01-03   16.3
D     2023-01-01   18.3
2023-01-02   20.3
2023-01-03   22.3
>>> 
>>> df2.shape
(12, 1)
>>> 
>>> df2.index
MultiIndex(('2023-01-01', 'A'),
('2023-01-01', 'B'),
('2023-01-01', 'C'),
('2023-01-01', 'D'),
('2023-01-02', 'A'),
('2023-01-02', 'B'),
('2023-01-02', 'C'),
('2023-01-02', 'D'),
('2023-01-03', 'A'),
('2023-01-03', 'B'),
('2023-01-03', 'C'),
('2023-01-03', 'D'),
names='date', 'place')
>>> 
>>> df2.columns
Index('value', dtype='object')
>>> 
>>> df2.loc('2023-01-02', 'A')
value    2.3
Name: (2023-01-02 00:00:00, A), dtype: float64
>>> 
>>> df2.loc'2023-01-02'
value
place       
A        2.3
B        8.3
C       14.3
D       20.3
>>> 
>>> df2.xs('A', level='place')
value
date             
2023-01-01    0.3
2023-01-02    2.3
2023-01-03    4.3
>>>

**樞軸聚合運算**
使用pivot_table()，對數據幀進行樞軸聚合運算：
>>> df3 = df1.pivot_table(index='date', columns='place', aggfunc='sum')
>>> df3
value                  
place          A     B     C     D
date                              
2023-01-01   0.3   6.3  12.3  18.3
2023-01-02   2.3   8.3  14.3  20.3
2023-01-03   4.3  10.3  16.3  22.3
>>> 
>>> df3.shape
(3, 4)
>>> 
>>> df3.to_numpy()
array( 0.3,  6.3, 12.3, 18.3,
2.3,  8.3, 14.3, 20.3,
4.3, 10.3, 16.3, 22.3)
>>> 
>>> df3.index
DatetimeIndex('2023-01-01', '2023-01-02', '2023-01-03', dtype='datetime64ns', name='date', freq=None)
>>> 
>>> df3.columns
MultiIndex(('value', 'A'),
('value', 'B'),
('value', 'C'),
('value', 'D'),
names=None, 'place')
>>> 
>>> df1.pivot_table(index='date', columns='place', values='value', aggfunc='sum').columns
Index('A', 'B', 'C', 'D', dtype='object', name='place')
>>> 
>>> df3'value'.columns
Index('A', 'B', 'C', 'D', dtype='object', name='place')
>>> 
>>> df3('value', 'A')
date
2023-01-01    0.3
2023-01-02    2.3
2023-01-03    4.3
Name: (value, A), dtype: float64
>>> 
>>> df3.stack().shape
(12, 1)
>>> df2.unstack().shape
(3, 4)
>>> 

**歸併運算**
使用merge()，對數據幀進行歸併運算：
>>> sr_A = df3('value', 'A')
>>> sr_B = df3('value', 'B')
>>> sr_C = df3('value', 'C')
>>> sr_D = df3('value', 'D')
>>> merge_date = lambda x, y: pd.merge(x, y, on='date')
>>> df3 = merge_date(merge_date(sr_A, sr_B), merge_date(sr_C, sr_D)) \
...     .rename_axis(None, 'place', axis=1)
>>> 
>>> pd.merge(df_A, df_B.drop('2023-01-02'), on='date')
value      
A     B
date                  
2023-01-01   0.3   6.3
2023-01-03   4.3  10.3
>>> 
>>> pd.merge(df_A, df_B.drop('2023-01-02'), on='date', how='left')
value      
A     B
date                  
2023-01-01   0.3   6.3
2023-01-02   2.3   NaN
2023-01-03   4.3  10.3
>>> 

**繪圖輸出**
例子代碼生成的條形圖。
用matplotlib為數據幀繪製條形圖：
>>> df_axis0, df_axis1 = df3.shape
>>> df_columns = df3.to_dict(orient='list')
>>> df_index = df3.index.to_list()
>>> x = np.arange(df_axis0)
>>> width = 1/(df_axis1 + 1)
>>> mult = 0
>>> 
>>> fig, ax = plt.subplots(layout='constrained')
>>> for name, value in df_columns.items():
...     offset = width * mult
...     rects = ax.bar(x + offset, value, width, label=name1)
...     ax.bar_label(rects, padding=3)
...     mult += 1
... 
Text(0, 3, '0.3'), Text(0, 3, '2.3'), Text(0, 3, '4.3')
Text(0, 3, '6.3'), Text(0, 3, '8.3'), Text(0, 3, '10.3')
Text(0, 3, '12.3'), Text(0, 3, '14.3'), Text(0, 3, '16.3')
Text(0, 3, '18.3'), Text(0, 3, '20.3'), Text(0, 3, '22.3')
>>> ax.set_xticks(x + width*(df_axis1 - 1)/2,
...     x.strftime('%Y-%m-%d') for x in df_index)
, , 
>>> ax.legend()
>>> ax.grid(axis='y', linestyle=':')
>>> ax.set_axisbelow(True)
>>> plt.show()
>>>

**CSV文件操作**
導出和導入CSV文件：
>>> df2.to_csv('dftest.csv', float_format='%.1f')
>>> 
>>> df4 = pd.read_csv('dftest.csv', index_col=0,1)
>>> df4.shape
(12, 1)
在不指定header參數即採用其缺省值之時，從導入文件標頭即第一橫行推導出數據的縱列名字。在導入文件時採用index_col參數，指定用作索引即橫行標籤的縱列。在標頭的縱列數大於數據記錄的縱列數之時，標頭末位多出的縱列被設置為NaN；在標頭的縱列數小於數據記錄的縱列數之時，數據記錄首位多出的縱列被用作索引；在標頭的縱列數等於數據記錄的縱列數，並且未指定索引列之時，則自動增加默認的沒有名字的範圍索引作為索引；在標頭的縱列數等於數據記錄的縱列數，並且已指定索引列之時，採用指定縱列作為索引。
在導入文件時可以通過usecols參數指定選用的縱列子集。如果所導入的文件沒有標頭，則需要指定header=None，還可以使用names參數，為導入數據指定縱列名字。如果所導入的文件有標頭，並且要使用names參數指定替代它的縱列名字，則需要同時指定header=0。在導出文件時索引總是被導出的，可以使用index_label參數，為沒有名字的索引縱列補充指定其名字。在導入pandas所導出的文件之時，需要指定索引列來避免再次自動增加缺省索引。
當數據幀是記錄格式，故而columns方向採用了多重索引之時，所導出的CSV文件的標頭有多個橫行，以這裡的數據幀df3為例，在導入它之時需要指定header=0, 1和index_col=0，但不能同時使用usecols或names參數：
>>> df3.to_csv('dftest.csv', float_format='%.1f')
>>> 
>>> df4 = pd.read_csv('dftest.csv', header=0, 1, index_col=0)
>>> df4.shape
(3, 4)
使用util-linux工具組成員column來查看導出的CSV文件：
$ cat dftest.csv | column -s, -o, -t 
,value,value,value,value
place     ,A    ,B    ,C    ,D
date      ,     ,     ,     ,
2023-01-01,0.3  ,6.3  ,12.3 ,18.3
2023-01-02,2.3  ,8.3  ,14.3 ,20.3
2023-01-03,4.3  ,10.3 ,16.3 ,22.3
這裡的前兩橫行的除了第一縱列之外的縱列，指定多層索引的縱列名字為('value', 'A'), ('value', 'B'), ('value', 'C'), ('value', 'D')。第一縱列的前兩單元，指定了諸縱列columns的多重索引自身的名字列表為None, 'place'，而第一縱列的第三單元，指定了索引index的日期時間索引自身的名字為'date'。

**JSON文件操作**
導出和導入JSON文件：
>>> df3.to_json('dftest.json', orient='index', date_format='iso', date_unit='s')
>>> 
>>> df4 = pd.read_json('dftest.json', orient='index')
>>> df4.shape
(3, 4)
>>> 
>>> df4.columns
Index('('value', 'A')', '('value', 'B')', '('value', 'C')', '('value', 'D')', dtype='object')
>>> 
>>> df4.columns = pd.MultiIndex.from_tuples(eval(x) for x in df4.columns)
>>> df4.columns.names = None, 'place'
>>> df4.index.name = 'date'
這裡指定了orient（方向）為'index'（索引），即採用橫行為主次序；這裡指定了日期時間格式為ISO 8601標準格式，並且指定了時間單位為秒。JSON對象的鍵只能是字符串，在導出為JSON文件之時，元組被轉換成字符串；而在導入它之時，需要通過eval()將其再轉換為元組。
使用jq語言實現jq來查看導出的JSON文件：
$ cat dftest.json | jq
{
"2023-01-01T00:00:00": {
"('value', 'A')": 0.3,
"('value', 'B')": 6.3,
"('value', 'C')": 12.3,
"('value', 'D')": 18.3
},
"2023-01-02T00:00:00": {
"('value', 'A')": 2.3,
"('value', 'B')": 8.3,
"('value', 'C')": 14.3,
"('value', 'D')": 20.3
},
"2023-01-03T00:00:00": {
"('value', 'A')": 4.3,
"('value', 'B')": 10.3,
"('value', 'C')": 16.3,
"('value', 'D')": 22.3
}
}
在導出的數據中，不包含諸縱列columns的多重索引自身的名字列表即None, 'place'，不包含索引index自身的名字即'date'。

**HDF5文件操作**
導出和導入HDF5文件基於了PyTables：
>>> df3.to_hdf('dftest.h5', key='df3', mode='w')
>>> df.to_hdf('dftest.h5', key='df', mode='a')
>>> 
>>> df4 = pd.read_hdf('dftest.h5', key='df3')
>>> df4.shape
(3, 4)
這裡通過鍵（key）參數，指定了與數據幀相對應的在HDF5文件中的群組（Group），對它採用了缺省的'fixed'（固定）存儲格式。文件打開模態'w'是為“寫”（write）即“新建”，而打開模態'a'是為“附加”（append）。
使用hdf5-tools工具組成員h5ls來查看導出的HDF5文件：
$ h5ls dftest.h5
df                       Group
df3                      Group
$ h5ls -r -d dftest.h5/df3
/axis0_label0            Dataset {4}
Data:
0, 0, 0, 0
/axis0_label1            Dataset {4}
Data:
0, 1, 2, 3
/axis0_level0            Dataset {1}
Data:
"value"
/axis0_level1            Dataset {4}
Data:
"A", "B", "C", "D"
/axis1                   Dataset {3}
Data:
1672531200000000000, 1672617600000000000, 1672704000000000000
/block0_items_label0     Dataset {4}
Data:
0, 0, 0, 0
/block0_items_label1     Dataset {4}
Data:
0, 1, 2, 3
/block0_items_level0     Dataset {1}
Data:
"value"
/block0_items_level1     Dataset {4}
Data:
"A", "B", "C", "D"
/block0_values           Dataset {3, 4}
Data:
0.3, 6.3, 12.3, 18.3, 2.3, 8.3, 14.3, 20.3, 4.3, 10.3, 16.3, 22.3
這裡的HDF5文件中的日期時間表示，是以納秒為單位的UNIX時間紀元的時間戳。這種存儲格式保存了數據幀的兩個軸和所有的塊，它採用的軸編號0和1與編程API所用的NumPy軸編號相反。這裡的df3存儲只有一個塊，這個塊的items的內容同於axis0。下面查看df存儲的情況，它的四個縱列被整合（consolidate）為三個塊，其items的並集同於axis0:
$ h5ls -r dftest.h5/df
/axis0                   Dataset {4}
/axis1                   Dataset {12}
/block0_items            Dataset {1}
/block0_values           Dataset {12, 1}
/block1_items            Dataset {2}
/block1_values           Dataset {12, 2}
/block2_items            Dataset {1}
/block2_values           Dataset {1/Inf}
保存HDF5文件還可採用'table'（表格）格式，HDF5文件中這種存儲格式的群組，可以直接在其上進行查詢和過濾：
>>> df3.to_hdf('dftest.h5', key='df3t', format='table', mode='a')
>>> 
>>> pd.read_hdf('dftest.h5', key='df3t', where='index > 20230101', columns=('value', 'A'), ('value', 'C'))
value      
place          A     C
date                  
2023-01-02   2.3  14.3
2023-01-03   4.3  16.3
查看HDF5文件中的這種存儲格式：
$ h5ls dftest.h5/df3t
_i_table                 Group
table                    Dataset {3/Inf}
$ h5ls -r -d dftest.h5/df3t/table
df4/table                Dataset {3/Inf}
Data:
{1672531200000000000, 0.3,6.3,12.3,18.3},
{1672617600000000000, 2.3,8.3,14.3,20.3},
{1672704000000000000, 4.3,10.3,16.3,22.3}
這裡的_i_table/index群組存儲了PyTables的tables.index模塊所存取的諸多內容。

**netCDF文件操作**
導出和導入netCDF文件可以藉助xarray，它依賴於pandas，它通過netcdf4-python支持導入導出netCDF-4格式數據，通過SciPy支持其他版本netCDF格式。xarray能夠在自身的數據陣列（DataArray）與pandas的序列之間，在自身的數據集（Dataset）與pandas的數據幀之間，進行相互轉換：
>>> import xarray as xr
>>> 
>>> df2.to_xarray()
Size: 152B
Dimensions:  (date: 3, place: 4)
Coordinates:- date     (date) datetime64ns 24B 2023-01-01 2023-01-02 2023-01-03- place    (place) object 32B 'A' 'B' 'C' 'D'
Data variables:
value    (date, place) float64 96B 0.3 6.3 12.3 18.3 ... 4.3 10.3 16.3 22.3
>>> 
>>> df3.to_xarray()
Size: 120B
Dimensions:         (date: 3)
Coordinates:- date            (date) datetime64ns 24B 2023-01-01 2023-01-02 2023-01-03
Data variables:
('value', 'A')  (date) float64 24B 0.3 2.3 4.3
('value', 'B')  (date) float64 24B 6.3 8.3 10.3
('value', 'C')  (date) float64 24B 12.3 14.3 16.3
('value', 'D')  (date) float64 24B 18.3 20.3 22.3
>>> 
>>> df2.to_xarray().to_netcdf('dftest.nc')
>>> 
>>> df4 = xr.open_dataset('dftest.nc').to_dataframe()
>>> df4.shape
(12, 1)
這裡的從堆疊格式的數據幀df2轉換出來的數據集可以導出至netCDF文件，而從記錄格式的數據幀df3轉換出來的數據集因變量名字而不能直接導出至netCDF文件。
使用netcdf-bin工具組成員ncdump來查看導出的netCDF文件：
$ ncdump dftest.nc
netcdf dftest {
dimensions:
	date = 3 ;
	place = 4 ;
variables:
	double value(date, place) ;
		value:_FillValue = NaN ;
	int64 date(date) ;
		date:units = "days since 2023-01-01 00:00:00" ;
		date:calendar = "proleptic_gregorian" ;
	string place(place) ;
data:
value =
0.3, 6.3, 12.3, 18.3,
2.3, 8.3, 14.3, 20.3,
4.3, 10.3, 16.3, 22.3 ;
date = 0, 1, 2 ;
place = "A", "B", "C", "D" ;
}
$ ncdump -k dftest.nc
netCDF-4
$ h5ls -r -d dftest.nc
/                        Group
/date                    Dataset {3}
Data:
0, 1, 2
/place                   Dataset {4}
Data:
"A", "B", "C", "D"
/value                   Dataset {3, 4}
Data:
0.3, 6.3, 12.3, 18.3, 2.3, 8.3, 14.3, 20.3, 4.3, 10.3, 16.3, 22.3
這裡的日期時間表示遵循了Climate and Forecast Metadata Conventionsdead-url=no，採用的時間單位為距離某個指定的開始日期時間的日數，曆法為前推格里高利曆。ncdump的輸出所稱謂的變量，代表相同類型的值的多維陣列。這裡有三個變量：value是數據變量，date和place是座標變量。變量聲明指定了變量的數據類型、名字和以維度名字列表描述的形狀，例如double value(date, place)，維度名字可以是x, y, z, t等等，而這裡的維度名字同於對應的座標變量名字。

**SQL關係數據庫操作**
下面通過SQLAlchemy將數據幀導出和導入於SQL關係數據庫SQLite：
>>> from sqlalchemy import create_engine
>>> engine = create_engine("sqlite:///dftest.db")
>>> 
>>> df2.to_sql(name='df2', con=engine)
12
>>> 
>>> with engine.connect() as conn:
...     df4 = pd.read_sql('SELECT * FROM df2', conn)
... 
>>> 
>>> df4.shape
(12, 3)
>>> df4'date' = pd.to_datetime(df4'date')
>>> df4 = df4.set_index('date', 'place')
>>> df4.shape
(12, 1)
使用SQLite的命令行界面來查看導出的數據庫文件：
$ echo '.tables' | sqlite3 dftest.db
df2
$ echo '.schema df2' | sqlite3 dftest.db
CREATE TABLE df2 (
	date DATETIME, 
	place TEXT, 
	value FLOAT
);
CREATE INDEX ix_df2_date ON df2 (date);
CREATE INDEX ix_df2_place ON df2 (place);
$ echo 'SELECT * FROM df2' | sqlite3 dftest.db
2023-01-01 00:00:00.000000|A|0.3
2023-01-01 00:00:00.000000|B|6.3
2023-01-01 00:00:00.000000|C|12.3
2023-01-01 00:00:00.000000|D|18.3
2023-01-02 00:00:00.000000|A|2.3
2023-01-02 00:00:00.000000|B|8.3
2023-01-02 00:00:00.000000|C|14.3
2023-01-02 00:00:00.000000|D|20.3
2023-01-03 00:00:00.000000|A|4.3
2023-01-03 00:00:00.000000|B|10.3
2023-01-03 00:00:00.000000|C|16.3
2023-01-03 00:00:00.000000|D|22.3
SQLite將SQL的數據類型DATETIME硬性指定為親和NUMERIC，而NUMERIC可以按其適合情況轉換為五個存儲類別（NULL、INTEGER、REAL、TEXT或BLOB）之一，這裡的數據幀df2的date縱列中的數據，被轉換成為INTEGER、REAL和TEXT三者中的TEXT即文本字符串。

**參見**
 NumPy- R語言- Scikit-learn- statsmodels- 數值分析軟件

**引用**

**延伸閱讀**
 dead-url=no - dead-url=no - dead-url=no - dead-url=no 

**外部鏈接**
 dead-url=no