記事ディレクトリ
1 グループ集計
1.1 データの集約とグループ化
単純な累積手法によりデータセットの概要を把握できますが、特定のタグやインデックス部分に対して累積分析を実行する必要がある場合が多く、この場合には groupby が必要です。「グループ化」という名前は SQL データベース言語のコマンドから借用されたものですが、R 言語フレームを発明したハドリー ウィッカムのアイデア (分割、適用、結合) を参照する方が適切かもしれません。
分割、適用、結合
古典的な分割、適用、結合操作。「適用」は合計関数です。
以下の図は、GroupBy プロセスを明確に示しています。分割ステップでは、指定されたキーに従って DataFrame をいくつかのグループに分割します。適用ステップでは、各グループに関数 (通常は累積関数、変換関数、またはフィルター関数) を適用します。結合ステップでは、各グループの結果を単一の出力配列に結合します。
これは、前に紹介した一連のマスキング、蓄積、マージ操作を通じても実現できますが、中間のセグメンテーション プロセスを明示的に公開する必要がないことを認識することが重要です。また、GroupBy では (多くの場合)、各グループの合計、平均、カウント、最小、およびその他の累積値を計算するために必要なコードは 1 行だけです。GroupBy の目的は、これらのステップを抽象化することです。操作を全体として見る限り、ユーザーは最下位レベルでの計算方法を知る必要はありません。
groupby:(by=なし、as_index=True)
by :グループ化の基準、groupby のグループを決定するために使用されます
as_index : 集計出力の場合、DataFrame の場合のみ、グループ ノートによってインデックス付けされたオブジェクトを返します。
df = pd.DataFrame(
{
'fruit': ['apple', 'banana', 'orange', 'apple', 'banana'], # 名称
'color': ['red', 'yellow', 'yellow', 'cyan', 'cyan'], # 颜色
'price': [8.5, 6.8, 5.6, 7.8, 6.4], # 单价
'count': [3, 4, 6, 5, 2]}) # 购买总量,单位/斤
df['total'] = df['price'] * df['count']
print(df)
出力:
fruit color price count total
0 apple red 8.5 3 25.5
1 banana yellow 6.8 4 27.2
2 orange yellow 5.6 6 33.6
3 apple cyan 7.8 5 39.0
4 banana cyan 6.4 2 12.8
ここでの戻り値は DataFrame オブジェクトではなく、DataFrameGroupBy オブジェクトであることに注意してください。このオブジェクトの魅力は、いくつかのデータ セットを非表示にする特別な形式の DataFrame と考えることができますが、累積関数を適用しないと計算されないことです。この「遅延評価」アプローチにより、ほとんどの一般的な累積操作を、ユーザーにとってほとんど透過的な (操作が存在しないかのように感じられる) 方法で非常に効率的に実装できます。
この結果を取得するには、DataFrameGroupBy オブジェクトに累積関数を適用します。これにより、対応する適用/結合ステップが完了し、結果が生成されます。
print(df.groupby('fruit').sum())
"""
price
fruit
apple 16.3
banana 13.2
orange 5.6
"""
sum() は、利用可能な多数のメソッドの 1 つにすぎません。Pandas または NumPy の任意の累積関数、または任意の有効な DataFrame オブジェクトを使用できます。
GroupBy オブジェクト
GroupBy の最も重要な操作は、aggregate、filter、transform、apply (蓄積、フィルタリング、変換、適用) です。これらの内容については後ほど詳しく紹介します。ここで、GroupBy の基本的な操作方法をいくつか紹介します。
(1) カラムごとに値を取得します。GroupBy オブジェクトは、DataFrame と同様に、列単位の値もサポートし、変更された GroupBy オブジェクトを返します。次に例を示します。
import pandas as pd
df = pd.DataFrame({
'fruit': ['apple', 'banana', 'orange', 'apple', 'banana'],
'color': ['red', 'yellow', 'yellow', 'cyan', 'cyan'],
'price': [8.5, 6.8, 5.6, 7.8, 6.4],
'count': [3, 4, 6, 5, 2]})
df['total'] = df['price'] * df['count']
# 查看类型
print(df.groupby('fruit'))
print(df.groupby('fruit').sum())
"""group 对象"""
for name, group in df.groupby('fruit'):
print('分组名:\t', name) # 输出组名
print('分组数据:\n', group) # 输出数据块
# 对数量与总价
print(df.groupby('fruit')[['count', 'total']].apply(lambda x: x.sum()))
アグリゲーション (agg)
関数名 | 説明する |
---|---|
カウント | グループ内の非 NA 値の数 |
和 | NA 以外の値の合計 |
平均 | 非NA値の平均 |
中央値 | 非 NA 値の中央値 |
標準、変数 | 標準偏差と分散 |
最小、最大 | NAではない最小値、最大値 |
プロッド | NA 以外の値の積 |
最初の最後 | 最初と最後の NA 以外の値 |
例えば: |
import numpy as np
# 多分组数据进行多个运算
print(df.groupby('fruit').aggregate({
'price': 'min', 'count': 'max'}))
"""
如果我现在有个需求,计算每种水果最高价和最低价的差值,
1.上表中的聚合函数不能满足于我们的需求,我们需要使用自定义的聚合函数
2.在分组对象中,使用我们自定义的聚合函数
"""
# 定义一个计算差值的函数
def diff_value(arr):
return arr.max() - arr.min()
print(df.groupby('fruit')['price'].agg(diff_value))
price count
fruit
apple 7.8 5
banana 6.4 4
orange 5.6 6
fruit
apple 0.7
banana 0.4
orange 0.0
Name: price, dtype: float64
フィルター
"""分组后我们也可以对数据进行过滤<自定义规则过滤>"""
def filter_func(x):
print('price:\t', [x['price']])
print('price:\t', type(x['price']))
print('price:\t', x['price'].mean())
return x['price'].mean() > 6 # 过滤平均价格大于6
print(df)
print('分组价格的平均数: \t', df.groupby('fruit')['price'].mean())
# 分组之后进行过滤,不影响原本的数据
print(df.groupby('fruit').filter(filter_func))
1.2 データの結合
1.2.1 pd.merge データのマージ
-
単一または複数のキーに基づいて異なる DataFrame の行を結合します
-
データベースのような接続操作
-
pd.merge :(left, right, how='inner',on=None,left_on=None, right_on=None )
左: マージ時の左側の DataFrame
right: マージ時の右側の DataFrame
方法: マージ方法、デフォルトは 'inner'、'outer'、'left'、'right'
on: マージする必要がある列名は両側にある必要があり、左右の列名の交差部分が接続キーとして使用されます。
left_on: 結合キーとして使用される左側のデータフレームの列
right_on: 結合キーとして使用されるデータフレームの右列
-
内部結合: 両方のテーブルのキーの共通部分を結合します。
-
外部完全結合: 両方のテーブルのキーの和集合の和集合
-
Left join left: 左側のテーブルのすべてのキーを結合します。
-
右結合右: 右テーブルのすべてのキーの結合
import pandas as pd
import numpy as np
left = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K3'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({
'key': ['K0', 'K1', 'K2', 'K3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left,right,on='key') #指定连接键key
key A B C D
0 K0 A0 B0 C0 D0
1 K1 A1 B1 C1 D1
2 K2 A2 B2 C2 D2
3 K3 A3 B3 C3 D3
left = pd.DataFrame({
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left,right,on=['key1','key2']) #指定多个键,进行合并
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
#指定左连接
left = pd.DataFrame({
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left, right, how='left', on=['key1', 'key2'])
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
#指定右连接
left = pd.DataFrame({
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left, right, how='right', on=['key1', 'key2'])
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
3 K2 K0 NaN NaN C3 D3
デフォルトは「inner」(内部)です。つまり、結果のキーは交差です。
接続方法の指定方法
「Outer」(外側)、結果のキーは和集合です
left = pd.DataFrame({
'key1': ['K0', 'K0', 'K1', 'K2'],
'key2': ['K0', 'K1', 'K0', 'K1'],
'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']})
right = pd.DataFrame({
'key1': ['K0', 'K1', 'K1', 'K2'],
'key2': ['K0', 'K0', 'K0', 'K0'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']})
pd.merge(left,right,how='outer',on=['key1','key2'])
"""
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K0 K1 A1 B1 NaN NaN
2 K1 K0 A2 B2 C1 D1
3 K1 K0 A2 B2 C2 D2
4 K2 K1 A3 B3 NaN NaN
5 K2 K0 NaN NaN C3 D3
"""
重複する列名の処理
- パラメータの接尾辞: デフォルトは _x、_y です。
df_obj1 = pd.DataFrame({
'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data' : np.random.randint(0,10,7)})
df_obj2 = pd.DataFrame({
'key': ['a', 'b', 'd'],
'data' : np.random.randint(0,10,3)})
print(pd.merge(df_obj1, df_obj2, on='key', suffixes=('_left', '_right')))
#运行结果
"""
data_left key data_right
0 9 b 1
1 5 b 1
2 1 b 1
3 2 a 8
4 2 a 8
5 5 a 8
"""
インデックスによる結合
- パラメータ left_index=True または right_index=True
# 按索引连接
df_obj1 = pd.DataFrame({
'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1' : np.random.randint(0,10,7)})
df_obj2 = pd.DataFrame({
'data2' : np.random.randint(0,10,3)}, index=['a', 'b', 'd'])
print(pd.merge(df_obj1, df_obj2, left_on='key', right_index=True))
#运行结果
"""
data1 key data2
0 3 b 6
1 4 b 6
6 8 b 6
2 6 a 0
4 3 a 0
5 0 a 0
"""
1.2.2 pd.concat データのマージ
軸に沿って複数のオブジェクトを結合する
NumPy 連結
import numpy as np
import pandas as pd
arr1 = np.random.randint(0, 10, (3, 4))
arr2 = np.random.randint(0, 10, (3, 4))
print(arr1)
print(arr2)
print(np.concatenate([arr1, arr2]))
print(np.concatenate([arr1, arr2], axis=1))
#运行结果
"""
# print(arr1)
[[3 3 0 8]
[2 0 3 1]
[4 8 8 2]]
# print(arr2)
[[6 8 7 3]
[1 6 8 7]
[1 4 7 1]]
# print(np.concatenate([arr1, arr2]))
[[3 3 0 8]
[2 0 3 1]
[4 8 8 2]
[6 8 7 3]
[1 6 8 7]
[1 4 7 1]]
# print(np.concatenate([arr1, arr2], axis=1))
[[3 3 0 8 6 8 7 3]
[2 0 3 1 1 6 8 7]
[4 8 8 2 1 4 7 1]]
"""
pd.concat
- 軸方向の指定に注意、デフォルトはaxis=0
- Joinはマージ方法を指定、デフォルトはouter
- Seriesマージ時に行インデックスが重複していないか確認
df1 = pd.DataFrame(np.arange(6).reshape(3,2),index=list('abc'),columns=['one','two'])
df2 = pd.DataFrame(np.arange(4).reshape(2,2)+5,index=list('ac'),columns=['three','four'])
print(pd.concat([df1,df2])) #默认外连接,axis=0
print(pd.concat([df1,df2],axis='columns')) #指定axis=1连接
print(pd.concat([df1,df2],axis=1,join='inner'))
"""
one two three four
a 0.0 1.0 NaN NaN
b 2.0 3.0 NaN NaN
c 4.0 5.0 NaN NaN
a NaN NaN 5.0 6.0
c NaN NaN 7.0 8.0
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
one two three four
a 0 1 5 6
c 4 5 7 8
"""
1.3 再発明
1.3.1 スタック
- 列インデックスを行インデックスにローテーションして階層インデックス作成を完了します
- データフレーム -> シリーズ
data = pd.DataFrame(np.arange(6).reshape((2, 3)),
index=pd.Index(['Ohio', 'Colorado'], name='state'),
columns=pd.Index(['one', 'two', 'three'],
name='number'))
print(data)
result = data.stack()
print(result)
#输出:
"""
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
state number
Ohio one 0
two 1
three 2
Colorado one 3
two 4
three 5
dtype: int32
"""
1.3.2 アンスタック
- 階層インデックスを展開する
- シリーズ -> データフレーム
- デフォルトの操作は内部インデックス、つまり level=1 です。
# 默认操作内层索引
print(stacked.unstack())
# 通过level指定操作索引的级别
print(stacked.unstack(level=0))
"""
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5
"""
2つの時系列
時系列データは、構造化データの重要な形式です。複数の時点で観察または測定された時間は、時間の経過とともにシーケンスを形成します。多くの場合、時系列には固定頻度があります。つまり、データ ポイントは特定のパターンに従って定期的に表示されます (15 秒ごとなど)。時系列が不規則になることもあります。時系列データの意味は、特定のアプリケーション シナリオによって異なります。主に以下の種類で構成されます。
- タイムスタンプ、特定の瞬間。
- 2007 年 1 月や 2010 年全体などの固定期間 (期間)。
- 開始タイムスタンプと終了タイムスタンプで表される時間間隔 (間隔)。期間は間隔の特殊なケースと考えることができます。
2.1 Python 組み込み時間モジュール
Python 標準ライブラリには、日付と時刻のデータ型とカレンダー機能が含まれています。主に日時、時刻、カレンダーのモジュールを使用します。datetime.datetime (datetime とも略される) は、最も一般的に使用されるデータ型です。
from datetime import datetime
now = datetime.now()
now
"""
datetime.datetime(2023, 11, 1, 21, 25, 38, 531179)
"""
now.year, now.month, now.day
"""
(2023, 11, 1) # 返回元组
"""
datetime は日付と時刻をミリ秒単位で保存します。timedelta は、2 つの datetime オブジェクト間の時間差を表します。
delta = datetime(2023, 1, 7) - datetime(2022, 6, 24, 8, 15)
delta
"""
datetime.timedelta(days=196, seconds=56700)
"""
delta.days
# 输出:
"""
196
"""
from datetime import timedelta
start = datetime(2022, 1, 7)
start + timedelta(31)
"""
datetime.datetime(2022, 2, 7, 0, 0)
"""
2.1.2 文字列と日時を相互に変換する
stamp = datetime(2023, 10, 1)
stamp
#输出
datetime.datetime(2023, 10, 1, 0, 0)
"""将datetime对象转换成字符串"""
# 强制转换
str(stamp)
# 格式化
stamp.strftime("%Y-%m-%d %H:%M:%S")
#输出:
'2023-10-01 00:00:00'
"""将字符串转换成datetime对象"""
datetime.strptime('2023/5/20', '%Y/%m/%d')
#输出
datetime.datetime(2023, 5, 20, 0, 0)
d = ['2023/7/20', '2023/7/21', '2023/7/22']
pd.to_datetime(d) # 转换后是 DatetimeIndex 对象
index = pd.to_datetime(d + [None])
index # NaT 时间序列的缺失值
pd.isnull(index)
index.dropna() #删除NaT 时间序列的缺失值
2.2 時系列
シンボル | 意味 | 例 |
---|---|---|
%a |
曜日の短縮名 | 'Wed' |
%A |
完全な営業日名 | 'Wednesday' |
%w |
曜日番号 - 0 (日曜日) ~ 6 (土曜日) | '3' |
%d |
月の日 (ゼロパディング) | '13' |
%b |
月の短縮名 | 'Jan' |
%B |
完全な月名 | 'January' |
%m |
年の月 | '01' |
%y |
世紀のない年 | '16' |
%Y |
世紀のある年 | '2016' |
%H |
24時間時計の時間 | '17' |
%I |
12時間時計の時間 | '05' |
%p |
朝午後 | 'PM' |
%M |
分 | '00' |
%S |
2番 | '00' |
%f |
マイクロ秒 | '000000' |
%z |
タイムゾーン対応オブジェクトの UTC オフセット | '-0500' |
%Z |
タイムゾーン名 | 'EST' |
%j |
一年のある日 | '013' |
%W |
年間の週 | '02' |
%c |
現在のロケールの日付と時刻の表現 | 'Wed Jan 13 17:00:00 2016' |
%x |
現在のロケールの日付表現 | '01/13/16' |
%X |
現在のロケールの時間表現 | '17:00:00' |
%% |
リテラル% 文字 |
'%' |
dates = [datetime(2020, 12, 12), datetime(2020, 12, 13),
datetime(2020, 12, 14), datetime(2020, 12, 15),
datetime(2020, 12, 16)]
ts = pd.Series(np.random.randn(5), index=dates)
ts # 创建一个 Series 对象, 以时间为行索引
#输出
2020-12-12 0.376037
2020-12-13 0.426828
2020-12-14 0.050578
2020-12-15 0.302734
2020-12-16 -0.068885
dtype: float64
ts.index # 行索引是时间序列索引对象
输出
DatetimeIndex(['2020-12-12', '2020-12-13', '2020-12-14', '2020-12-15',
'2020-12-16'],
dtype='datetime64[ns]', freq=None)
2.2.1 値
ts
2020-12-12 0.376037
2020-12-13 0.426828
2020-12-14 0.050578
2020-12-15 0.302734
2020-12-16 -0.068885
dtype: float64
ts[::2] # 取步长
2020-12-12 0.376037
2020-12-14 0.050578
2020-12-16 -0.068885
dtype: float64
2.2.2 推定時間
ts1 = pd.Series(
data=np.random.randn(1000),
index=pd.date_range('2020/01/01', periods=1000)
)
ts1
2020-01-01 -0.700438
2020-01-02 -1.961004
2020-01-03 -0.226558
2020-01-04 -0.594778
2020-01-05 -1.394763
...
2022-09-22 0.025743
2022-09-23 0.784406
2022-09-24 -0.930995
2022-09-25 0.974117
2022-09-26 -1.625869
Freq: D, Length: 1000, dtype: float64
ts1['2021-05-18': '2021-05-27'] # 切片
2021-05-18 -0.487663
2021-05-19 -0.529925
2021-05-20 -0.316952
2021-05-21 0.476325
2021-05-22 -1.006280
2021-05-23 0.438202
2021-05-24 1.505284
2021-05-25 0.523409
2021-05-26 -1.139620
2021-05-27 0.573387
Freq: D, dtype: float64
2.2.3 時間範囲の指定
pd.date_range('2020-01-01', '2023-08-28')
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
'2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
'2020-01-09', '2020-01-10',
...
'2023-08-19', '2023-08-20', '2023-08-21', '2023-08-22',
'2023-08-23', '2023-08-24', '2023-08-25', '2023-08-26',
'2023-08-27', '2023-08-28'],
dtype='datetime64[ns]', length=1336, freq='D')
pd.date_range(start='2020-01-01', periods=100) # 往后推算100天
pd.date_range(end='2023-08-28', periods=100) # 往前推算100天
リサンプリングと周波数変換
リサンプリングとは、時系列をある周波数から別の周波数に変換するプロセスを指します。高周波データを低周波に集約することをダウンサンプリングと呼び、低周波データを高周波に変換することをアップサンプリングと呼びます。すべてのリサンプリングがこれら 2 つの大きなカテゴリに分類できるわけではありません。たとえば、W-WED (毎週水曜日) を W-FRI に変換することは、ダウンサンプリングでもアップサンプリングでもありません。
すべての pandas オブジェクトには、さまざまな周波数変換タスクの主な関数である resample メソッドがあります。resample には、groupby に似た API があります。resample を呼び出すと、データをグループ化し、集計関数を呼び出すことができます。resample は、非常
に大規模な時系列を処理するために使用できる、柔軟で効率的なメソッドです。
pd.date_range(start='2023-08-28', periods=10, freq='BH') # freq指定时间频率
出力:
DatetimeIndex(['2023-08-28 09:00:00', '2023-08-28 10:00:00',
'2023-08-28 11:00:00', '2023-08-28 12:00:00',
'2023-08-28 13:00:00', '2023-08-28 14:00:00',
'2023-08-28 15:00:00', '2023-08-28 16:00:00',
'2023-08-29 09:00:00', '2023-08-29 10:00:00'],
dtype='datetime64[ns]', freq='BH')