LoginSignup
129
174

[python / pandas] DataFrame を扱う人が覚えておきたい、ちょっとレアな便利技16選

Last updated at Posted at 2024-03-20

概要

  • そこまでメジャーではない(?)
  • けど、覚えておくと実装時間やコードの行数を大幅削減できる!

という便利な技をご紹介します!

「そういえばpandasってあんなこともできたような気がするな。」
「自力で実装する前に調べてみようかな?」

と気付けると、時短 & コード量削減できる可能性が生まれます。
ではでは、お楽しみください!!

Environment

以下の環境で動作確認を行いました。

項目 version など
OS Ubuntu 22.04.3 LTS (Google Colaboratory)
Python 3.10.12
pandas 1.5.3

便利技たち!

0. 一覧

下に行けば行くほど、珍しく、使い道がよくわからなくなっていきます😂
これ見たことないな〜、っていうところだけでも見ていっていただけると嬉しいです^^

※「レア度」は、星が多くなるほど珍しい、もしくは難解な処理であることを意味しています。(独断と偏見により設定)

No レア度 メソッド
1 df.isin()
2 df.columns = ["列名", ..]
3 df.between( , )
4 ★★ df.to_dict(orient="records")
5 ★★ pd.to_numeric(df["列名"], errors="coerce").notna()
6 ★★ df.apply(func, axis=1)
7 ★★★ df.groupby()["列名"].size()
8 ★★★ df.groupby()["列名"].head(n)
9 ★★★ df.filter(like="部分列名")
10 ★★★★ set_index(["列名"], append=True)
11 ★★★★ df.groupby()["列名"].rank()
12 ★★★★ df.groupby()["列名"].shift()
13 ★★★★ df.groupby()["列名"].transform()
14 ★★★★★ df["列名"].apply(pd.Series)
15 ★★★★★ df.explode()
16 ★★★★★ df.unstack()

1. レア度:★

  • ごく簡単に実装できるもの
  • もしくは、頻出メソッド

1-1. isin - 複数候補に対する完全一致判定

動作確認のために、以下のようなサンプルデータを用意します。

colaboratory
df = pd.DataFrame({
    "item_name": [
        "松屋サイダー",
        "pakemon card",
        "パリウス",
        "あいぽん12",
        "イーロン茶"
    ],
    "price": [
        140,
        350,
        4_000_000,
        150_000,
        120
    ]
})
df

# 結果
	item_name       price
0	松屋サイダー         140
1	pakemon card      350
2	パリウス         4000000
3	あいぽん12       150000
4	イーロン茶           120

あなたは今、上記 df という DataFrame の中から飲み物 ("イーロン茶"、"松屋サイダー"、"QQレモン" のうちいずれか) が欲しいとします。
こういう時に isin を使うのです!

colaboratory
drink_names = ["イーロン茶", "松屋サイダー", "QQレモン"]

# 複数の候補に対して、完全一致判定をする
drink_index = df["item_name"].isin(drink_names)
df_extracted: pd.DataFrame = df[drink_index]
df_extracted

# 見事「飲み物」をゲットした!
	item_name	price
0	松屋サイダー  140
4	イーロン茶    120

この結果は、0行目と4行目だけが ["イーロン茶", "松屋サイダー", "QQレモン"] のどれかと一致したということ。

ちなみに drink_index の中身はこうなってる。

colaboratory
drink_index

# 以下の通り
0     True
1    False
2    False
3    False
4     True
Name: column_name1, dtype: bool

True 行だけが DataFrame から抽出される仕組みなりぃ。

関連記事

1-2. df.columns = - カラム名を一気にrename

今度は、 DataFrame の列名をまとめて全部書き換えるときに使えるワザです。

colaboratory
df = pd.DataFrame({
    "item_name": [
        "松屋サイダー",
        "pakemon card",
        "パリウス",
        "あいぽん12",
        "イーロン茶"
    ],
    "price": [
        140,
        350,
        4_000_000,
        150_000,
        120
    ]
})
df

# 結果
	item_name     price
0	松屋サイダー      140
1	pakemon card    350
2	パリウス      4000000
3	あいぽん12     150000
4	イーロン茶         120

さっきのこいつの、列名を書き換えてしまいましょう。

colaboratory
# えいっ!と代入
df.columns = ["商品名", "価格"]
df

# こうなる
	商品名           価格
0	松屋サイダー       140
1	pokemon card     350
2	パリウス       4000000
3	あいぽん12      150000
4	イーロン茶          120

真面目に1列ずつ df.rename(columns={"item_name": "商品名", "price": "価格"}) してあげても良いんですけどね。 columns に直代入した方が楽。

1-3. df.between( , ) - 2つの引数でレコードを絞り込む

別に、日付のデータである必要はないのですが、わかりやすいので日付の文字列でサンプルを示します。

colaboratory
# サンプルデータ
df: pd.DataFrame = pd.DataFrame(
    {
        "date": [
            "2023-12-03",
            "2023-12-04",
            "2023-12-05",
            "2023-12-06",
            "2023-12-07",
            "2023-12-08",
            "2023-12-09",
        ]
    }
)

これを、 between で絞り込んでみよう!

colaboratory
# between の両端に挟まれるデータを抽出
df[df['date'].between("2023-12-05", "2023-12-08")]

# 実行結果
	date
2	2023-12-05
3	2023-12-06
4	2023-12-07
5	2023-12-08

ちなみに、単純に between を実行した直後はこうなってるよ。

colaboratory
df['date'].between("2023-12-05", "2023-12-08")

# 実行結果
0    False
1    False
2     True
3     True
4     True
5     True
6    False
Name: date, dtype: bool

この結果を見ると、引数に指定した値と完全一致するレコードも True で返ってくるみたいですね。

2. レア度:★★

  • まぁまぁ簡単に使えるけど、あんまり見かけない。

2-1. to_dict(orient="records") - DataFrame に対する loop を高速化

今は numpypandas がありますし、できれば loop せずにベクトル演算、ブロードキャスト演算したいですね。

でも、どうしても大量の loop を回したい時はこうします。

colaboratory
from typing import List


dict_data: List[dict] = df.to_dict(orient="records")
for dict in dict_data:
    # loop しないといけない処理

# from_dict で DataFrame に戻す
df_converted: pd.DataFrame = pd.from_dict(dict_data)

こうしないと、 DataFrame の loop は結構遅いんですよ|・`ω・´)チラリッ
(なんと400倍もの速度差になったりします)

関連記事

2-2. to_numeric(series , errors="coerce") - 数値型に変換できない値を回避

たとえば以下のような DataFrame の "numeric?" 列を float 型に変換したいとします。

colaboratory
df = pd.DataFrame(
    {
        "numeric?": [1, 2, "3", "4", "5.0", ".1", "l"],
        "alpha": ["a", "b", "c", "d", "e", "f", "g"]
    }
)

# 実行結果
	numeric?  alpha
0	1         a
1	2         b
2	3         c
3	4         d
4	5.0       e
5	.1        f
6	l         g

実行すると、最終行の "l" (エル) が float 型に変換できない値だったため、エラーになります。

colaboratory
df["numeric?"].astype(float)

# 実行結果
ValueError: could not convert string to float: 'l'

このような大量のデータがある時に、 to_numeric を使うと、うまく float に変換できる値だけを取り出すことができます。

こんな感じ!

colaboratory
pd.to_numeric(df["numeric?"], errors="coerce").notna()

# 結果
0     True
1     True
2     True
3     True
4     True
5     True
6    False
Name: numeric?, dtype: bool

これで6行目が数値に変換できないとわかったので、除外するのも簡単ですね(*´v`)

colaboratory
# あとはこうする
astypable_indices = pd.to_numeric(df["numeric?"], errors="coerce").notna()
df.loc[astypable_indices, "numeric?"].astype(float)

# 実行結果
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    0.1
Name: numeric?, dtype: float64

関連記事

2-3. apply(func, axis=1) - 1行ごとに関数を実行

DataFrame 内の1行1行を、1つのSeriesとして関数の引数に渡して処理したいときに使います。

たとえば、以下のような関数に1つの Series を渡してみます。

colaboratory
import pandas as pd


# apply メソッドのサンプル
def sample_concat_str(series: pd.Series) -> str:
    """
    pd.Series を受け取り、 series 内の文字列を全て連結して返却

    Example
    ------
    >>> sample_series = pd.Series(["123", "456", "789"])
    >>> sample_concat_str(sample_series)
    "123-456-789"
    """
    return "-".join(list(series["list"]))


df: pd.DataFrame = pd.DataFrame({
    "dummy": [1, 2, 3],
    "list": [
        ["abc", "def"],
        ["123", "456", "789"],
        ["hoge"]
    ]
})

series: pd.Series = df.iloc[1]
series

# series の中身
dummy                  2
list     [123, 456, 789]
Name: 1, dtype: object

この series を関数に渡すと以下のようになります。

colaboratory
sample_concat_str(series)

# 結果
'123-456-789'

これを、Series 1個だけではなくて、 DataFrame にある分全部に対して実行させたい場合に以下のようにします。

colaboratory
df.apply(sample_concat_str, axis=1)

# 実行結果
0        abc-def
1    123-456-789
2           hoge
dtype: object

apply をもっと幅広く応用したい方には、以下の記事もおすすめ( *¯ ꒳¯*)✨

3. レア度:★★★

  • 見かけることははっきりいってほとんどないですが、比較的動作が理解しやすい / イメージしやすい処理
  • 覚えておくと、活用できるチャンスはきっと廻ってくる(気がする

3-1. groupby と size のコンボ

SQL で言うところの、 GROUP BY 結果に対する COUNT(<列名>) が実行できます!
普通に使う機会があると思うので、覚えておいて損はないです。

colaboratory
df.groupby(["GROUP BY の対象列", "対象列2", ...])["COUNT したい列名"].size()

# Example
#    「各ユーザーが各商品を何回買ったのか」を知りたい時...
df.groupby(["username", "購入商品"])["購入商品"].size()

実際の使い方はこんな感じ。

colaboratory
df = pd.DataFrame(
    {
        "username": [
            "たけち",
            "マティス",
            "かつみ",
            "マティス",
            "ありか",
            "ありか",
            "たけち",
            "たけち",
            "ありか",
            "マティス",
        ],
        "購入商品": [
            "キズサソリ",
            "おいしいおゆ",
            "アドバンテージボール",
            "おいしいおゆ",
            "おいしいおゆ",
            "たべかけ",
            "キズサソリ",
            "キズサソリ",
            "たべかけ",
            "アドバンテージボール"
        ]
    }
)
df

# 実行結果
	username	購入商品
0	たけち       キズサソリ
1	マティス	    おいしいおゆ
2	かつみ       アドバンテージボール
3	マティス      おいしいおゆ
4	ありか       おいしいおゆ
5	ありか       たべかけ
6	たけち       キズサソリ
7	たけち       キズサソリ
8	ありか       たべかけ
9	マティス      アドバンテージボール

ここで、誰が何を何個買ったかを調べるとき、これでOK!

colaboratory
df.groupby(["username", "購入商品"])["購入商品"].size()

# 実行結果
username  購入商品      
ありか     おいしいおゆ         1
         たべかけ            2
かつみ    アドバンテージボール   1
たけち    キズサソリ           3
マティス   おいしいおゆ         2
         アドバンテージボール   1
Name: 購入商品, dtype: int64

3-2. groupby().head(n) - グループ別に先頭から n 件データ抽出

以下のようなマラソンの順位表を考えます。

colaboratory
df: pd.DataFrame = pd.DataFrame(
    [
        {"名前": "桔梗", "性別": "female", "time": "04:58"},
        {"名前": "雅人", "性別": "male",   "time": "05:02"},
        {"名前": "真由", "性別": "female", "time": "05:15"},
        {"名前": "慎二", "性別": "male",   "time": "05:32"},
        {"名前": "達人", "性別": "male",   "time": "05:41"},
        {"名前": "祥平", "性別": "male",   "time": "06:12"}
    ]
)
df

# 実行結果
	名前	性別	time
0	桔梗	female	04:58
1	雅人	male	05:02
2	真由	female	05:15
3	慎二	male	05:32
4	達人	male	05:41
5	祥平	male	06:12

この順位表から、性別ごとに(男女別々に)タイムの短かった方を2人ずつ抜き出したいとしましょう。

これで行けます!

colaboratory
df.groupby(["性別"])[["名前", "性別", "time"]].head(2)

# 実行結果
	名前	性別	time
0	桔梗	female	04:58
1	雅人	male	05:02
2	真由	female	05:15
3	慎二	male	05:32

この応用として、例えば「地区」列などがあったときに、「地区ごと」かつ「性別ごと」に上位 n 件を抜き出すこともできるので、結構応用範囲は広いです。

関連記事

3-3. filter - カラム名に対する部分一致検索

こいつは超便利です!
絶対に頭の片隅に入れておいた方がいい!

こんな DataFrame があったとき、

colaboratory
df = pd.DataFrame({
    "feat_1": np.arange(0, 5),
    "feat_2": np.arange(0, 5),
    "feat_3": np.arange(0, 5),
    "feat_4": np.arange(0, 5),
    "feat_5": np.arange(0, 5),
    "label_1": np.arange(0, 5),
    "label_2": np.arange(0, 5),
    "label_3": np.arange(0, 5)
})
df

# 実行結果
   feat_1  feat_2  feat_3  feat_4  feat_5  label_1  label_2  label_3
0       0       0       0       0       0        0        0        0
1       1       1       1       1       1        1        1        1
2       2       2       2       2       2        2        2        2
3       3       3       3       3       3        3        3        3
4       4       4       4       4       4        4        4        4

ここから、列名に "feat_" が入っている列だけを取得したいとしましょう。

愚直に df[["feat_1", "feat_2", ...]] と書いてもいいのですが、以下のようにするともっと簡単に取得できます。

colaboratory
df.filter(like="feat_")

# 実行結果
   feat_1  feat_2  feat_3  feat_4  feat_5
0       0       0       0       0       0
1       1       1       1       1       1
2       2       2       2       2       2
3       3       3       3       3       3
4       4       4       4       4       4

これ、覚えておいて損ないですよね?(*´▽`*)
これぞ時短&バグの削減ですよ。

関連記事

4. レア度:★★★★

  • 初見だと、何が起こるか全くイメージできない
  • 理解して使えるようになっておくと、処理を自分で実装しなくて済むので、非常に時短になる
  • 使うチャンスはもしかしたらないかもしれないけど、、、もしその時が来たらかなり役に立つ

4-1. set_index([], append=True) - MultiIndex化

  • MultiIndex って、作り方あんまりイメージが沸かないんですけど、、、これはそこそこ覚えやすいです。
  • set_index はみなさん使ってると思うんですけど、ポイントは append=True です。
df.set_index(["index に追加したい column 名"], append=True)

MultiIndex に良く出くわす方には、ぜひ覚えておいてほしい珠玉の一品(?)です。

実際の使い方はこんな感じです(サンプルデータがしょぼくてすみません。゚・(´^ω^`)・゚。)

colaboratory
df = pd.DataFrame({
    "date": ["2023-12-31", "2023-01-01", "2023-01-02"],
    "腕立て伏せの回数": [197, 256, 0],
})
df

# 実行結果
	date	腕立て伏せの回数
0	2023-12-31	        197
1	2023-01-01	        256
2	2023-01-02	          0

この DataFrame の date 列を index に追加して、 MultiIndex な DataFrame を作りましょう。

これでOK!

colaboratory
df.set_index(["date"], append=True)

# 実行結果
		        腕立て伏せの回数
          date	
0	2023-12-31	          197
1	2023-01-01	          256
2	2023-01-02	            0

4-2. groupby と rank のコンボ

グループごとに、特定の列の値の順位を返却してくれます。

df.groupby(["GROUP BY の対象列", "対象列2", ...])["順位を知りたい列名"].rank()

# Example
#    「各ユーザー×商品のグループごとの、購入個数ランキング」が欲しい時...
df.groupby(["user_id", "商品名"])["購入個数"].rank(ascending=False)

更に具体的にを見てみましょう。

colaboratory
df = pd.DataFrame(
    {
        "username": [
            "たけち",
            "ありか",
            "ありか",
            "たけち",
            "たけち",
            "ありか",
        ],
        "購入商品": [
            "キズサソリ",
            "おいしいおゆ",
            "おいしいおゆ",
            "おいしいおゆ",
            "キズサソリ",
            "おいしいおゆ",
        ],
        "購入個数": [
            12,
            1,
            10,
            6,
            4,
            2,
        ]
    }
)

df

# 出力
	username  購入商品    購入個数
0	たけち	  キズサソリ	     12
1	ありか      おいしいおゆ       1
2	ありか      おいしいおゆ      10
3	たけち      おいしいおゆ       6
4	たけち      キズサソリ        4
5	ありか      おいしいおゆ       2

このようなデータに対して適用すると、以下のように順位を出力してくれます。

colaboratory
df.groupby(["username", "購入商品"])["購入個数"].rank(method="first", ascending=False)

# 出力
0    1.0
1    3.0
2    1.0
3    1.0
4    2.0
5    2.0
Name: 購入個数, dtype: float64

何が嬉しいのかちょっとわかりにくいので、元のデータフレームに代入してみましょう。
こうなります。

colaboratory
df["購入数順位"] = (
    df.groupby(["username", "購入商品"])["購入個数"]
    .rank(method="first", ascending=False)
)
df.sort_values(by=["username", "購入数順位"])

# 出力結果
	username  購入商品    購入個数	購入数順位
2	ありか    おいしいおゆ        10      1.0
5	ありか    おいしいおゆ         2      2.0
1	ありか    おいしいおゆ         1      3.0
3	たけち    おいしいおゆ	      6      1.0
0	たけち    キズサソリ         12      1.0
4	たけち    キズサソリ          4      2.0

sort_values で、グループ化した列名と、ランキングが入力されている列を指定すると、人目にも理解しやすい結果が出力されますよ(*´v`)
どなたかの役に立てれば嬉しいです。

これって、SQLだと何に当たるのかな...?

関連記事

4-3. groupby() と shift() - グループごとに n 行ずらす

これも使いどころがちょっと難しいのですが、たとえば、先程のような商品購入ログであれば、特定ユーザが同名商品を「前回購入した日」をリストに追加したいときなどに使えます。

サンプルデータはこんな感じ。

colaboratory
df = pd.DataFrame(
    {
        "username": [
            "たけち",
            "ありか",
            "ありか",
            "たけち",
            "たけち",
            "ありか",
        ],
        "購入商品": [
            "キズサソリ",
            "おいしいおゆ",
            "おいしいおゆ",
            "おいしいおゆ",
            "キズサソリ",
            "おいしいおゆ",
        ],
        "購入日": [
            "2023-12-01",
            "2023-12-03",
            "2023-12-05",
            "2023-12-05",
            "2023-12-07",
            "2023-12-10",
        ]
    }
)

df

# 出力
	username 購入商品      購入日
0	たけち   キズサソリ    2023-12-01
1	ありか   おいしいおゆ   2023-12-03
2	ありか   おいしいおゆ   2023-12-05
3	たけち   おいしいおゆ   2023-12-05
4	たけち   キズサソリ    2023-12-07
5	ありか   おいしいおゆ   2023-12-10

このデータに対して、「前回購入日」列を追加します。

colaboratory
df["前回購入日"] = (
    df.groupby(["username", "購入商品"])["購入日"]
    .shift(1)
)
df.sort_values(by=["username", "購入商品", "購入日"])

# 出力
	username  購入商品   購入日        前回購入日
1	ありか    おいしいおゆ   2023-12-03   NaN
2	ありか    おいしいおゆ   2023-12-05   2023-12-03
5	ありか    おいしいおゆ   2023-12-10   2023-12-05
3	たけち    おいしいおゆ   2023-12-05   NaN
0	たけち    キズサソリ    2023-12-01   NaN
4	たけち    キズサソリ    2023-12-07   2023-12-01

こんな感じで、前回購入した日付を一気に出力できます。
shift は、通常の DataFrame に対する shift と同じで負の値や2以上の値も入れられるので、もしかしたらもっと活用できる価値があるかも。

関連記事

以下の記事によると、処理速度的にも申し分ない早さみたいですよ(*´v`)

4-4. groupby() と transform()

groupby がめっちゃ出てきているのでついでに紹介するぜ!(*´▽`*)
しかし、これも使いどころが難しい。。。

以下の記事がわかりやすいので、これはこの記事に解説をお願いしたい(o*。_。)o

5. レア度:★★★★★

  • ハッキリ言って、いつ使うのか全くイメージが沸かない...( ˘·ω·˘ ).。oஇ
  • でも、自分で実装しようとしたら大変手間のかかる処理を、関数で一発で実現できるので大助かり!
  • 使い方は忘れてもいいけど、「そういえば、pandas で用意されているメソッドであんなことできたはずだよね...」って思い出せれば、かなりアドバンテージになります!

5-1. df["列名"].apply(pd.Series)

Series の 1要素内に list型 が入っている際に、それらを展開してくれる。

たとえば、こんな困ったデータがあったとする。

colaboratory
df = pd.DataFrame({
    "a": [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]],
    "b": [["a", "b", "c", "d", "e"], ["f", "g", "h", "i", "j"]]
})

# 出力
    a                 b
0	[1, 2, 3, 4, 5]	  [a, b, c, d, e]
1	[6, 7, 8, 9, 10]  [f, g, h, i, j]

"a" 列にも "b" 列にも、1要素にまるまるリスト型が入り込んでいる。
これをなんとかして、1要素につき値は一つだけ、という形に変換したい。
そんな時にこうする。

df["b"].apply(pd.Series)

# 実行結果
	0	1	2	3	4
0	a	b	c	d	e
1	f	g	h	i	j

上記は "b" 列を展開した場合の話。 "a" 列も展開したければ、別途 .apply(pd.Series) してあげるとよい。

関連記事

5-2. explode - 爆ぜろ!

こちらも、Series の 1要素内に list型 が入っている際に、それらを展開してくれる。
df["列名"].apply(pd.Series) との大きな違いは、各配列の要素数が異なる(ジャグ配列の場合)でも上手く処理してくれるところだろうか。

colaboratory
df = pd.DataFrame({'A': [[0, 1, 2], 'foo', [], [3, 4]],
                   'B': [5, 1, 1, 6],
                   'C': ['a', np.nan, [], 'd']})
df

# 実行結果
           A  B      C
0  [0, 1, 2]  5      a
1        foo  1    NaN
2         []  1     []
3     [3, 4]  6      d

爆発(して縦に分裂)します。

colaboratory
# "A"列に入っている配列を、行方向に分割 (爆発?)
df.explode('A')

# 実行結果
     A  B      C
0    0  5      a
0    1  5      a 
0    2  5      a
1  foo  1    NaN
2  NaN  1     []
3    3  6      d
3    4  6      d

explodeメソッドの引数に指定した列以外は、単純に複製される。

メソッド名も動作も楽しいけど、使い所が難しい。。。

関連記事

5-3. unstack - 潰れろ!

MultiIndexSeries のいずれかの Index を、 DataFrame の列名へと変換してくれる。

サンプルデータはこちら。
鬼と桃太郎の腕立て伏せ競争?の結果データを考える()

colaboratory
index = pd.MultiIndex.from_tuples([
    ('ももたろ', '2023-03-04'), ('ももたろ', '2023-03-05'), ('ももたろ', '2023-03-06'),
    ('', '2023-03-04'), ('', '2023-03-06')
])
series = pd.Series(
    [591, 528, 580, 1209, 1221],
    index=index,
    name="腕立て伏せの回数"
)
series

# 出力
ももたろ   2023-03-04     591
         2023-03-05     528
         2023-03-06     580
       2023-03-04    1209
         2023-03-06    1221
Name: 腕立て伏せの回数, dtype: int64

ここで、後ろから数えて1つめ(level=-1)の Index を新たな列名として DataFrame にすると、こうなる。

colaboratory
series.unstack(level=-1)

# 出力
         2023-03-04	2023-03-05	2023-03-06
ももたろ        591.0      528.0       580.0
           1209.0        NaN      1221.0

これ、いつ使うんじゃ?という感じ。
でも私は仕事で使うことになった。頭の片隅に入れておいて損はない。

関連記事

公式サイトだけど、実行例も載ってるよ

あとがき

こういったメソッドを覚えておくと、難解な処理を自力で実装する手間が省けます。

その結果、以下のような効果が見込まれます!

  • ソース量が減る
  • 多重ネストが減る
  • バグが減る
  • 作業工数が減る
  • 引継ぎが少しスムーズになる???(これは微妙か?)

というわけで...みなさま、是非是非ご活用くださいませ(*´v`)

p.s.

もう多重ネストのコードは読みたくない...
iffor だけで力技で処理を書くのは、どうかどうか辞めてほしい。。。
スマートに行きましょう..._(-ω-`_)⌒)_

129
174
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
129
174