導入
category
は、pandas
の 機密データ タイプ です。テキスト データ.str.<methond>
と同様に、 アクセサ関数.cat.<method>
も備えています。
この記事では次のことを紹介します。
-
カテゴリデータとは何ですか?
-
機密データ
cat
処理方法 -
カテゴリデータを使用する理由
-
機密データ
cat
使用時の落とし穴
カテゴリデータとは何ですか?
カテゴリカル データは、特定の属性、種類、特性を持つ数値を表現しており、これもカテゴリカル データとして理解されます。たとえば、人口は性別によって男性と女性に分けられ、年齢によっては老人、中年、若者に分けられます。
コンピュータ言語では、通常、1 は男性、0 は女性を表すために数字を使用します。ただし、0 と 1 の間には関係はありません。pandas
はcategory
はカテゴリ データを表します。
カテゴリデータの作成
dtpye を使用すると、データの作成時に次のようにタイプを指定できます。
s = pd.Series(['a','b','c'],dtype='category')
s
------
0 a
1 b
2 c
dtype: category
Categories (3, object): ['a', 'b', 'c']
カテゴリデータを自動作成する
は、特定の動作条件下で分類タイプに自動的に変換されます。たとえば、cut
を使用したビニング操作によって返されるビニングは分類タイプです。
pd.Series(pd.cut(range(1,10,2),3))
-----------------
0 (0.992, 3.667]
1 (0.992, 3.667]
2 (3.667, 6.333]
3 (6.333, 9.0]
4 (6.333, 9.0]
dtype: category
Categories (3, interval[float64]): [(0.992, 3.667] < (3.667, 6.333] < (6.333, 9.0]]
カテゴリカルなデータ型の変換
次のようなastype
メソッドを使用して変換します。
s = pd.Series(['a','b','c'])
s
------
0 a
1 b
2 c
dtype: object
s.astype('category')
------
0 a
1 b
2 c
dtype: category
Categories (3, object): ['a', 'b', 'c']
カスタム分類データ
さらに、CategoricalDtype
を通じて機密データをカスタマイズすることもできます。カスタマイズされたタイプは、上記のすべての方法に適用できます。
たとえば、abc
以下の 3 つのカテゴリをカスタマイズし、順序を指定します。次に、 dtype
を通じてカスタム データ型を指定できます。 d
が定義された型 abc
にない場合は、次のように表示されます。空の。
from pandas.api.types import CategoricalDtype
# 自定义分类数据,有序
c= CategoricalDtype(categories=['a','b','c'],ordered=True)
pd.Series(list('abcabd'),dtype=c)
--------
0 a
1 b
2 c
3 a
4 b
5 NaN
dtype: category
Categories (3, object): ['a' < 'b' < 'c']
カテゴリデータの処理方法
分類の変更
カテゴリの名前は.cat.rename_categories()
で変更します。
s = pd.Series(['a','b','c'],dtype='category')
# 指定分类为x、y、z
s.cat.categories = ['x','y','z']
0 x
1 y
2 z
dtype: category
Categories (3, object): ['x', 'y', 'z']
# 列表形式:修改分类类型为mno
s.cat.rename_categories(['m','n','o'])
# 字典形式:
s.cat.rename_categories({'x':'m','y':'n','z':'o'})
0 m
1 n
2 o
dtype: category
Categories (3, object): ['m', 'n', 'o']
新しいカテゴリを追加
通过.cat.add_categories()
追加分类。
s.cat.add_categories(['r','t'])
0 x
1 y
2 z
dtype: category
Categories (5, object): ['x', 'y', 'z', 'r', 't']
カテゴリの削除
同様に、カテゴリを削除することもできます。 remove_categories
と remove_unused_categories
の 2 つの方法があります。
# 删除指定的分类r和t
s.cat.remove_categories(['r','t'])
# 自动删除未使用的分类
s.cat.remove_unused_categories()
注文
デフォルトでは、機密データは自動的に並べ替えられません。CategoricalDtype
または .cat.as_ordered
を通じて順序を設定できます。
# 有序设置
s.cat.as_ordered()
0 x
1 y
2 z
dtype: category
Categories (3, object): ['x' < 'y' < 'z']
# 无序设置
s.cat.as_unordered()
# 重新排序
s.cat.reorder_categories(['y','x','z'], ordered=True)
カテゴリ データ型を使用する理由
要約すると、category
を使用すると次のような利点があります。
-
メモリ使用量: 多くの繰り返し値を含む文字列列の場合、
category
により、データをメモリに保存するために必要なメモリ量を大幅に削減できます。< /span> -
実行パフォーマンス: 特定の操作の実行速度を向上させるためにいくつかの最適化が行われました
-
アルゴリズム ライブラリの適用性: 場合によっては、一部のアルゴリズム モデルでは
category
このタイプが必要です。たとえば、 と比較したlightgbm
の最適化ポイントの 1 つは、カテゴリ変数を処理できることです。モデルを構築するときに、どの変数を処理できるかを指定する必要があります。列はカテゴリ変数であり、設定されます。 に調整され、ハイパーパラメータとしてモデルに渡されます。xgboost
category
簡単な例です。
df_size = 100_000
df1 = pd.DataFrame(
{
"float_1": np.random.rand(df_size),
"species": np.random.choice(["cat", "dog", "ape", "gorilla"], size=df_size),
}
)
df1_cat = df1.astype({"species": "category"})
2 つ作成しましたDataFrame
、df1 には種が含まれており、タイプはobject
です。df1_cat は df1 をコピーしましたが、種をcategory
と入力します。
>> df1.memory_usage(deep=True)
Index 128
float_1 800000
species 6100448
dtype: int64
文字列を含む列がメモリ使用量の観点からどれほどコストがかかるかを直接確認できます。 種列の文字列は約 6 MB を占め、文字列が長い場合はさらに多くなります。
>> df1_cat.memory_usage(deep=True)
Index 128
float_1 800000
species 100416
dtype: int64
category
カテゴリに変換した後のメモリ使用量を確認します。 メモリ使用量が約 60 分の 1 となり、大幅な改善が見られます。コントラストがなければ害はありません。
これはcategory
を使用する利点の 1 つです。
category
の使用にはいくつかの落とし穴があります。
しかし、深い愛には大きな責任が伴います。category
注意すべき落とし穴がたくさんあります。ドン兄弟は、参考までに次の点をまとめています。
1. カテゴリ列の操作
まあ、この部分は誰もがもっと懸念している部分であるはずです。なぜなら、説明のつかないエラーレポートに遭遇したり、何かが間違っていると感じたりすることがよくあるからですが、問題がどこにあるのかはわかりません。
まず最初に説明します。使用するときは十分に注意する必要がありますcategory
。姿勢が間違っていると、危険な可能性があります。 object
に戻ります。 object
に戻すと、コードのパフォーマンスが低下し (型のキャストのコストが非常に高いため)、メモリが消費されます。
日次category
タイプのデータに直面する場合、変換などの操作を実行する必要があります。例を見てみましょう。アクセサーの category
型と object
型に対してそれぞれ同じ文字列の大文字化操作を実行する必要があります。 >方法です。 .str
カテゴリ以外の文字列の場合:
>> %timeit df1["species"].str.upper()
25.6 ms ± 2.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
カテゴリ文字列について:
>> %timeit df1_cat["species"].str.upper()
1.85 ms ± 41.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
結果は明らかです。この場合、スピードアップは約 14 倍です (内部最適化により、 .str.upper()
は分類の一意のカテゴリ値に対して 1 回だけ呼び出され、その結果に基づいてシリーズが構築されるためです。結果の場合) 値ごとに 1 回呼び出されます)。
をどのように理解しますか? 「animal」という列があり、そのカテゴリが cat
と dog
であるとします。10,000 個のサンプルと 4,000 個の cat
および 6000 個のサンプルがあるとします。 dog
。したがって、カテゴリ自体を処理すると、 cat
と dog
の 2 つのカテゴリをそれぞれ 1 回だけ処理することになり、合計 2 回解決されることになります。各値を処理する場合、10,000 倍のサンプル サイズが必要です。
時間に関してはいくつかの最適化が行われていますが、この方法の使用には依然としていくつかの問題があります。 。 。メモリ使用量を見てみましょう。
>> df1_cat["species"].str.upper().memory_usage(deep=True)
6100576
予期しない検出category
タイプがありません。 。結果は実際には object
型になり、データ圧縮の効果がなくなり、先ほどの 6MB のメモリ使用量に戻ります。
これは、str
を使用すると、元の category
タイプが object
に直接強制され、メモリ使用量が元に戻るためです。 、最初に特に注意するように言ったのはこれが理由です。
解決策は、カテゴリの値ではなくカテゴリ自体を直接操作することです。 cat メソッドを直接使用して変換操作を完了するには、次のようにします。
%timeit df1_cat["species"].cat.rename_categories(str.upper)
239 µs ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
この方が高速であることがわかります。カテゴリをオブジェクトに変換する時間が節約され、メモリ使用量が非常に少ないためです。したがって、これが最良のアプローチです。
2. カテゴリ列と結合する
上記と同じ例ですが、今回は habitat
列が追加され、 が species
に追加されます。 。 sanke
df2 = pd.DataFrame(
{
"species": ["cat", "dog", "ape", "gorilla", "snake"],
"habitat": ["house", "house", "jungle", "jungle", "jungle"],
}
)
df2_cat = df2.astype({"species": "category", "habitat": "category"})
前と同様に、category
バージョンのデータセットを作成し、object
文字列を含むバージョンを作成します。 2 つの object
列を結合しても、何が起こるかは誰もが知っているため、意味がありません。ただ object+ object= object
になるだけです。
オブジェクト列をカテゴリ列にマージします
上の例を続けます。
>> df1.merge(df2_cat, on="species").dtypes
float_1 float64
species object
habitat category
dtype: object
左側のものは df1
としてリストされ、species
は object
としてリストされ、右側のものは < としてリストされます。 /span> になることがわかります。 category+ object= object としてリストされます。マージすると、結果のマージされた列が は df2_cat
species
category
これは明らかにもう機能せず、以前の状態に戻ります。別の状況を試してみましょう。
2 つのカテゴリ列の結合
>> df1_cat.merge(df2_cat, on="species").dtypes
float_1 float64
species object
habitat category
dtype: object
結果:カテゴリ+ カテゴリ= オブジェクト?
誰かを殴りたくなるけど、心配しないで、その理由を見てみましょう。
マージで分類タイプを保存するには、2 つのcategory
タイプが同一である必要があります。 これは、pandas
の他のデータ型とは少し異なります。たとえば、すべての float64
列が同じデータ型である場合、何という違いでしょう。
データ型についてcategory
説明するとき、そのデータ型は実際にはその特定のカテゴリ内に存在する一連の値によって記述されるため、カテゴリには次のものが含まれます > ["cat", "dog", "mouse"]
はカテゴリに含まれるものと同じではありません["cheese", "milk", "eggs"]
。 snake
がもう 1 つ追加されたため、上の例は失敗しました。
したがって、次のように結論付けることができます。
-
category1+ category2=object
-
category1+ category1=category1
したがって、解決策は、2 つのカテゴリがまったく同じであるため、一方が他方と等しいとなります。
>> df1_cat.astype({"species": df2_cat["species"].dtype}).merge(
df2_cat, on="species"
).dtypes
float_1 float64
species category
habitat category
dtype: object
3. カテゴリ列のグループ化
をカテゴリ列でグループ化した場合、扱いを誤ると事故が発生し、Dataframe
が空値で埋められ、場合によっては死亡事故につながる可能性があります。 。
category
列でグループ化する場合、category
カテゴリのそれぞれのクラスに値が存在しない場合でも、デフォルトで各クラスがグループ化されます。
説明するための例。
habitat_df = (
df1_cat.astype({"species": df2_cat["species"].dtype})
.merge(df2_cat, on="species")
)
house_animals_df = habitat_df.loc[habitat_df["habitat"] == "house"]
ここではhabitat_df
を使用します。上記の例から、habitat
を house
にのみフィルタリングします。 a i=4> と は です。以下のグループ化結果を参照してください。 dog
cat
house
>> house_animals_df.groupby("species")["float_1"].mean()
species
ape NaN
cat 0.501507
dog 0.501023
gorilla NaN
snake NaN
Name: float_1, dtype: float64
はgroupby
で多数の null 値を取得しました。 デフォルトでは、category
列でグループ化する場合、pandas
は値が結果を返します になります。ちょっとした落とし穴として、データ型に存在しない値が多く含まれている場合、特に複数の異なる列でグループ化している場合、パフォーマンスが著しく低下します。 category
したがって、 解決策は、 observed=True
を groupby
を呼び出すと、データ内に値を持つグループのみが確実に取得されます。
>> house_animals_df.groupby("species", observed=True)["float_1"].mean()
species
cat 0.501507
dog 0.501023
Name: float_1, dtype: float64
4. カテゴリ欄のインデックス
引き続き上記の例を使用すると、groupby-unstack
、species
を列として、habitat
を行として使用してクロス集計が実装されます。どちらも category
タイプです。
>> species_df = habitat_df.groupby(["habitat", "species"], observed=True)["float_1"].mean().unstack()
>> species_df
species cat ape dog gorilla
habitat
house 0.501507 NaN 0.501023 NaN
jungle NaN 0.501284 NaN 0.501108
これに問題はないようです。読み続けてみましょう。このクロスタブに値 1 の新しい列new_col
を追加します。
>> species_df["new_col"] = 1
TypeError: 'fill_value=new_col' is not present in this Categorical's categories
通常の状況では、上記のコードはまったく問題ありませんが、ここでエラーが報告されます。
その理由は、: species
と habitat
のタイプが category
。 .unstack()
を使用すると、 species
インデックスが列インデックスに移動されます (pivot
クロス集計操作と同様)。追加された新しい列が species
のカテゴリ インデックスにない場合、エラーが報告されます。
要約すると、pandas
のcategory
型は非常に便利であり、パフォーマンスに優れた利点をもたらします。ただし、非常にデリケートでもあるため、使用するときは特に注意して、 category
タイプがプロセス全体を通じて変更されないようにし、 object
に戻らないようにしてください。この記事で注意すべき 4 つのポイント:
-
カテゴリ列の変換操作: カテゴリの値ではなくカテゴリ自体を直接操作します。これにより、カテゴリ的な性質が維持され、パフォーマンスが向上します。
-
カテゴリ列の結合: 結合するときは、
category
タイプを保持し、各dataframe
を結合することに注意してください。列内の分類タイプは正確に一致する必要があります。 -
カテゴリ列によるグループ化: デフォルトでは、結果がデータ内に存在しない場合でも、データ型の値ごとに結果が取得されます。設定
observed=True
で調整できます。 -
カテゴリ列のインデックス: インデックスのタイプが
category
の場合、カテゴリ変数で奇妙な相互作用が発生する可能性があることに注意してください。