Pandas Advanced: カテゴリカル データ処理

導入

category は、pandas 機密データ タイプ です。テキスト データ.str.<methond>と同様に、 アクセサ関数.cat.<method>も備えています。

この記事では次のことを紹介します。

  • カテゴリデータとは何ですか?

  • 機密データcat 処理方法

  • カテゴリデータを使用する理由

  • 機密データcat使用時の落とし穴

カテゴリデータとは何ですか?

カテゴリカル データは、特定の属性、種類、特性を持つ数値を表現しており、これもカテゴリカル データとして理解されます。たとえば、人口は性別によって男性と女性に分けられ、年齢によっては老人、中年、若者に分けられます。

コンピュータ言語では、通常、1 は男性、0 は女性を表すために数字を使用します。ただし、0 と 1 の間には関係はありません。pandascategory はカテゴリ データを表します。

カテゴリデータの作成

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_categoriesremove_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 つは、カテゴリ変数を処理できることです。モデルを構築するときに、どの変数を処理できるかを指定する必要があります。列はカテゴリ変数であり、設定されます。 に調整され、ハイパーパラメータとしてモデルに渡されます。 xgboostcategory

簡単な例です。

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」という列があり、そのカテゴリが catdog であるとします。10,000 個のサンプルと 4,000 個の cat および 6000 個のサンプルがあるとします。 dog。したがって、カテゴリ自体を処理すると、 catdog の 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 としてリストされ、speciesobject としてリストされ、右側のものは < としてリストされます。 /span> になることがわかります。 category+ object= object としてリストされます。マージすると、結果のマージされた列がdf2_cat speciescategory

これは明らかにもう機能せず、以前の状態に戻ります。別の状況を試してみましょう。

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を使用します。上記の例から、habitathouse にのみフィルタリングします。 a i=4> と です。以下のグループ化結果を参照してください。 dogcathouse

>> 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=Truegroupby を呼び出すと、データ内に値を持つグループのみが確実に取得されます。

>> house_animals_df.groupby("species", observed=True)["float_1"].mean()
species
cat    0.501507
dog    0.501023
Name: float_1, dtype: float64

4. カテゴリ欄のインデックス

引き続き上記の例を使用すると、groupby-unstackspecies を列として、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

通常の状況では、上記のコードはまったく問題ありませんが、ここでエラーが報告されます。

その理由は、: specieshabitat のタイプが category.unstack() を使用すると、 species インデックスが列インデックスに移動されます (pivot クロス集計操作と同様)。追加された新しい列が species のカテゴリ インデックスにない場合、エラーが報告されます。

要約すると、pandascategory 型は非常に便利であり、パフォーマンスに優れた利点をもたらします。ただし、非常にデリケートでもあるため、使用するときは特に注意して、 category タイプがプロセス全体を通じて変更されないようにし、 object に戻らないようにしてください。この記事で注意すべき 4 つのポイント:

  • カテゴリ列の変換操作: カテゴリの値ではなくカテゴリ自体を直接操作します。これにより、カテゴリ的な性質が維持され、パフォーマンスが向上します。

  • カテゴリ列の結合: 結合するときは、category タイプを保持し、各 dataframe を結合することに注意してください。列内の分類タイプは正確に一致する必要があります。

  • カテゴリ列によるグループ化: デフォルトでは、結果がデータ内に存在しない場合でも、データ型の値ごとに結果が取得されます。設定observed=Trueで調整できます。

  • カテゴリ列のインデックス: インデックスのタイプが category の場合、カテゴリ変数で奇妙な相互作用が発生する可能性があることに注意してください。

おすすめ

転載: blog.csdn.net/qq_39312146/article/details/134700881