データ分析のためのシェルワンライナー入門3(CSVファイルの列ずれを検出する)
こんにちは、データサイエンス部の荻です。
はじめに
データサイエンス部で隔週の社内勉強会を行っており、その内容を共有します。 今日は、データの受領・前処理・納品などで時短になる、簡単なシェルコマンドで解決する方法を紹介します。 前回の記事の続きになります。
- データ分析のためのシェルワンライナー入門1(カラム名が同じCSVファイル複数のファイルを一つのファイルにまとめる) - SENSY Product Dev Tech Blog
- データ分析のためのシェルワンライナー入門2(大量のエクセルファイルを全連結して、csvで吐く) - SENSY Product Dev Tech Blog
今回の課題
「顧客からデータを受け取る際、データが列づれしていた」 まずは、列づれしていないデータと列づれしているデータに分けたいですよね。 そこで、シェルコマンドを使用して、これらのデータを分ける処理を書きます。
コマンド
# 1. 列ずれを検出し、異常データを抽出し、別のcsvとして保存 awk -F ',' 'NR==1 {n=NF; print; next} NF!=n {if (!printed_header) {printed_header=1} print}' base_data.csv > abnormal_data.csv # 2. 正常なデータを抽出する awk -F ',' 'NR==FNR {if (NR==1) {n=NF} else if (NF!=n) {skip[NR]=1} next} !(FNR in skip)' base_data.csv base_data.csv > normal_data.csv
処理の内容
一行目にヘッダー情報が入っているCSVファイルに対して、#1は、1行目の列数と同じ列数ではないデータは出力するコマンド、#2は、1行目の列数と同じ列数のデータを出力するコマンドになっています。
詳しい解説
2 つコマンドの共通部分
両方のコマンドは awk を使用しており、CSV ファイルの処理を行っています。共通の要素は以下の通りです:
-F ',': カンマをフィールド区切り文字として指定しています。これにより、CSV ファイルの各列を正しく識別できます.NR==1 {...}: 最初の行(通常はヘッダー行)に対して特別な処理を行っています。この部分で、正常なデータの列数を変数nに保存しています.NF!=n: この条件を使用して、各行の列数が最初の行(ヘッダー)と一致するかどうかをチェックしています。これにより、列ずれを検出しています.base_data.csv: 両方のコマンドで同じ入力ファイルを使用しています。- 出力のリダイレクト(
>): 処理結果を新しい CSV ファイルに保存しています。

1. 列ずれを検出し、異常データを抽出
awk -F ',' 'NR==1 {n=NF; print; next} NF!=n {if (!printed_header) {printed_header=1} print}' base_data.csv > abnormal_data.csv
1. NR==1 {n=NF; print; next}:
- 最初の行(ヘッダー)の列数を
nに保存します. - ヘッダー行を出力し、次の行に進みます.
2. NF!=n: 現在の行の列数がnと異なる場合に実行します.
3. if (!printed_header) {printed_header=1}:
- 異常データのヘッダーが未出力の場合、フラグを設定します.
4. print: 列数が異なる行(異常データ)を出力します.
5. 結果をabnormal_data.csvに保存します.

2. 正常なデータを抽出する
awk -F ',' 'NR==FNR {if (NR==1) {n=NF} else if (NF!=n) {skip[NR]=1} next} !(FNR in skip)' base_data.csv base_data.csv > normal_data.csv
1. NR==FNR: 最初のファイル処理時に実行されるブロックです.
2. if (NR==1) {n=NF}: 最初の行の列数をnに保存します.
3. else if (NF!=n) {skip[NR]=1}:
- 列数が
nと異なる行の行番号をskip配列に記録します.
4. next: 次の行に進みます.
5. !(FNR in skip):
- 2 回目のファイル処理時、
skip配列に含まれない行を出力します.
6. 結果をnormal_data.csvに保存します.

使い方
1. csv ファイルが格納されているディレクトリに移動:
csv ファイルが保存されているディレクトリに移動してください。
2. コマンドの実行:
以下のコマンドを実行します。
# 1. 列ずれを検出し、異常データを抽出し、別のcsvとして保存 awk -F ',' 'NR==1 {n=NF; print; next} NF!=n {if (!printed_header) {printed_header=1} print}' base_data.csv > abnormal_data.csv # 2. 正常なデータを抽出する awk -F ',' 'NR==FNR {if (NR==1) {n=NF} else if (NF!=n) {skip[NR]=1} next} !(FNR in skip)' base_data.csv base_data.csv > normal_data.csv
注意点
- このコマンドを実行する前に、必ずデータのバックアップを取ってください。
おわりに
次回は、列づれが起きているデータの修正を行います。
今回使用したデータ
base_data.csv
| id | product_name | price | |
|---|---|---|---|
| 1 | nezjxhjjlx | 915 | |
| 2 | ohzcqyzeyx | 999 | |
| 3 | depmzyrkro | 963 | |
| 4 | zuzvdemlox | 1 | 66 |
| 5 | xccgxtkwwu | 941 | |
| 6 | scqsrrkeye | 1 | 10 |
| 7 | afdmkqorzd | 1 | 69 |
| 8 | pkgbulxghk | 1 | 68 |
| 9 | lvhxcyaooe | 1 | 85 |
| 10 | jhkyfwgoyd | 1 | 77 |
| 11 | akghzvgwda | 1 | 13 |
| 12 | agzqyusozz | 1 | 44 |
| 13 | nhuvkkgqmc | 1 | 58 |
| 14 | rnaeuodqnj | 1 | 71 |
| 15 | alacczwmwu | 952 | |
| 16 | lbyuegldfc | 969 | |
| 17 | pxmnyhrnum | 1 | 64 |
| 18 | uzqljcnzmu | 984 | |
| 19 | uejpchivil | 1 | 94 |
| 20 | oewfafsnhn | 1 | 48 |
| 21 | fflpkzlfzd | 901 | |
| 22 | llxgmdimzo | 908 | |
| 23 | qxxcabnqgl | 1 | 29 |
| 24 | fbunvaiend | 922 | |
| 25 | pxunapudfi | 942 | |
| 26 | xrheuqdkke | 1 | 35 |
| 27 | dghxopeoay | 937 | |
| 28 | przprezgtd | 934 | |
| 29 | wnjdrwblbm | 904 | |
| 30 | einujjrujy | 982 | |
| 31 | mvsstncjml | 1 | 22 |
| 32 | guuekhqgqh | 1 | 92 |
| 33 | rapizfkyab | 1 | 10 |
| 34 | qgcuepyfqw | 1 | 45 |
| 35 | wjmrzwmssz | 1 | 75 |
| 36 | aqsrrbvedt | 997 | |
| 37 | wcsfcbrvrh | 963 | |
| 38 | hmiydkcxul | 960 | |
| 39 | dggzwnurfb | 1 | 6 |
| 40 | ebnscvlmuw | 945 | |
| 41 | ofkybiahfq | 993 | |
| 42 | qtbqsfdiyb | 971 | |
| 43 | jugvjcirzp | 1 | 37 |
| 44 | fhddgugzgr | 1 | 33 |
| 45 | akvwtmnpsy | 985 | |
| 46 | acvbbkzcpn | 982 | |
| 47 | otkbkgebfr | 1 | 46 |
| 48 | opyaexdvda | 1 | 77 |
| 49 | joquydguxl | 1 | 96 |
| 50 | attynpacfd | 904 | |
| 51 | gunxrmgrln | 1 | 24 |
| 52 | bkyduicufz | 1 | 17 |
| 53 | recmqqzjbw | 972 | |
| 54 | mdavzhxmap | 1 | 63 |
| 55 | qfutefnivi | 1 | 77 |
| 56 | cjjacgwdvn | 1 | 39 |
| 57 | zszejxkshx | 921 | |
| 58 | pscdwcolom | 1 | 93 |
| 59 | ulhbkncwgm | 941 | |
| 60 | vwyspqyhlf | 1 | 33 |
| 61 | qbcffhqxrg | 906 | |
| 62 | vrterspkpy | 956 | |
| 63 | fmprdcspiy | 955 | |
| 64 | bkfpditgce | 1 | 80 |
| 65 | viigmvmpoi | 1 | 25 |
| 66 | gmckymhvli | 1 | 19 |
| 67 | zhvojwuraz | 901 | |
| 68 | yplkhncrxn | 1 | 87 |
| 69 | qbzmqqtrvn | 984 | |
| 70 | jgaagvebzj | 947 | |
| 71 | aewebeyjsk | 953 | |
| 72 | acqcumagvs | 1 | 66 |
| 73 | jstovobqxz | 1 | 77 |
| 74 | ntkwylrgww | 967 | |
| 75 | eyqlddqsnn | 997 | |
| 76 | swkkcrievy | 1 | 24 |
| 77 | gzthacvioz | 957 | |
| 78 | lfagwfilqt | 945 | |
| 79 | suqwuwjktu | 963 | |
| 80 | icdtjplyuj | 988 | |
| 81 | suxmljyorj | 965 | |
| 82 | fmbikviggc | 1 | 67 |
| 83 | vwyixummdv | 1 | 55 |
| 84 | szbghhwzyo | 1 | 1 |
| 85 | dvhjcfsfkf | 957 | |
| 86 | qgurculsye | 1 | 70 |
| 87 | wubdhfgudf | 963 | |
| 88 | xqfdbrbmdf | 972 | |
| 89 | arztqlkeky | 1 | 55 |
| 90 | wgncctzven | 985 | |
| 91 | utvvpdsfwl | 979 | |
| 92 | fkgjwhzixk | 1 | 54 |
| 93 | ckzdftoqys | 1 | 47 |
| 94 | ylgnrpcrdv | 1 | 94 |
| 95 | hpzpxyevsk | 1 | 10 |
| 96 | hxpxliqzwl | 900 | |
| 97 | oigmbbqyyr | 1 | 66 |
| 98 | xfgojcmugs | 1 | 95 |
| 99 | oefkvqukto | 931 | |
| 100 | xlnjwqpniw | 1 | 38 |
データ分析のためのシェルワンライナー入門2(大量のエクセルファイルを全連結して、csvで吐く)
こんにちは、データサイエンス部の荻です。
はじめに
データサイエンス部で隔週の社内勉強会を行っており、その内容を共有します。 今日は、データの受領・前処理・納品などで時短になる、簡単なシェルコマンドで解決する方法を紹介します。 前回の記事の続きになります。
データ分析のためのシェルワンライナー入門1(カラム名が同じCSVファイル複数のファイルを一つのファイルにまとめる) - SENSY Product Dev Tech Blog
今回の課題
「顧客からデータを受け取る際、データが大量でExcelファイルが送られてきた」 必要なデータを抽出するのに、ファイルを個別に処理するのは労力が大きので、一つのファイルにまとめたいですよね。 そこで、シェルコマンドを使用して、これらのファイルを一括でCSVに変換し、さらに1つのファイルにまとめる方法をご紹介します。
コマンド
for file in *.xlsx; do xlsx2csv "$file" "${file%.xlsx}_sheet" --all; done && (head -n 1 $(find *_sheet -name "*.csv" | head -n 1) && find *_sheet -name "*.csv" | xargs tail -q -n +2) > combined.csv
解説
1. ExcelファイルのCSV変換
for file in *.xlsx; do xlsx2csv "$file" "${file%.xlsx}_sheet" --all; done
- 目的: 現在のディレクトリ内のすべての
.xlsxファイルを.csvファイルに変換します。 - 動作:
- 現在のディレクトリ内のすべての
.xlsxファイルに対して処理を行います。 - ブック内に複数のシートが存在しても、シートごとに処理をします。
- 各
.xlsxファイルを対応する.csvファイルに変換します。
- 現在のディレクトリ内のすべての
2. CSVファイルの結合
(head -n 1 $(find *_sheet -name "*.csv" | head -n 1) && find *_sheet -name "*.csv" | xargs tail -q -n +2) > combined.csv
- 目的: 生成されたすべての
.csvファイルを1つのファイルに結合します。 - 動作:
使い方
1. 必要なツールのインストール:
まず、xlsx2csvコマンドを使用するために、以下のコマンドでインストールします。
sudo apt-get install xlsx2csv
2. Excelファイルが格納されているディレクトリに移動:
Excelファイルが保存されているディレクトリに移動してください。
3. コマンドの実行:
以下のコマンドを実行します。
for file in *.xlsx; do xlsx2csv "$file" "${file%.xlsx}_sheet" --all; done && (head -n 1 $(find *_sheet -name "*.csv" | head -n 1) && find *_sheet -name "*.csv" | xargs tail -q -n +2) > combined.csv
注意点
データ分析のためのシェルワンライナー入門1(カラム名が同じCSVファイル複数のファイルを一つのファイルにまとめる)
こんにちは、データサイエンス部の荻です。
はじめに
データサイエンス部で隔週の社内勉強会を行っており、その内容を共有します。
今日は、データの受領・前処理・納品などで時短になる、簡単なシェルコマンドで解決する方法を紹介します。
今回の課題
「顧客からデータを受け取る際、データが大量で10〜20ファイルに分割されたCSVファイルが送られてきた」
これらのデータを処理する際に、Jupyterで開くにしても、RDBに登録するにしても、データを一つのファイルにまとめたいですよね。
そこで、コピペで完了するシェルワンライナーを作成しましたので、その活用方法と使用方法をご紹介します。
コマンド
awk 'FNR==1 && NR!=1 {next} {print}' *.csv > combined.csv
使い方
解説
awkコマンド:
awkは、テキストファイルの情報を抽出し、加工するためのコマンドです。ファイルの一行一行に対して処理を実行します。
FNR==1 && NR!=1 {next}:FNRは、各ファイル内の行番号を示します。最初のファイルではFNRとNRは同じですが、2番目以降のファイルではFNRは1から再開します。NRは、全体の行番号を示します。最初のファイルの最初の行ではNRも1です。FNR==1 && NR!=1は、各ファイルの最初の行(ヘッダ行)をチェックしますが、最初のファイルのヘッダ行(NR==1)は除外します。つまり、2番目以降のファイルのヘッダ行はスキップされます。{next}は、条件が真の場合に次の行に進むことを指示します。つまり、2番目以降のファイルのヘッダ行は出力されません。
{print}:- これは、他の条件に当てはまらない行(つまり、最初のファイルのヘッダ行と、2番目以降のファイルのデータ行)を出力することを指示します。
*.csv > combined.csv:
補足
- シェルワンライナー: シェルで一行のコマンドで複雑な処理を実現する技法のことです。
データ分析で汎用的に使えるPython入門2:プロット&バーグラフの描写
SENSYデータサイエンティストチーム井上です。
今回はデータ分析で汎用的に使えるPythonの記事(第2回目)になります。 EDA(Explanatory Data Analysis, 探索的データ分析)の際、よく確認する内容を関数化しておくことで作業を効率化するシリーズになります!!
今回の目的
要点
- プロット&バーグラフの描写を出力する汎用的な関数を作成し、作業を効率化する
今回の関数で作成されるもの
- プロット&バーグラフ
- 左縦軸:プロットグラフ描写したい項目
- 右縦軸:バーグラフ描写したい項目
- 横 軸:時間
今回の目的の関数
def plot_and_bar_graph(df, time_column_name, plot_column_name, bar_column_name, plot_color="blue", bar_color="orange", title="", figsize=(10,6)): """ プロット&バーグラフの描写 Args: df (pd.DataFrame): 入力のデータフレーム time_column_name (str): 時間のカラム名 plot_column_name (str): プロットグラフ描写したいカラム名 bar_column_name (str): バーグラフ描写したいカラム名 plot_color (str): プロットグラフ描写の色 bar_color (str): バーグラフ描写の色 title (str): グラフのタイトル figsize (tuple): 図のサイズ """ # グラフの描画 fig, ax1 = plt.subplots(figsize=figsize) # プロットグラフ(左Y軸) ax1.plot(df[time_column_name], df[plot_column_name], color=plot_color, label=plot_column_name, marker="o") ax1.set_xlabel(time_column_name) ax1.set_ylabel(plot_column_name, color=plot_color) ax1.tick_params(axis="y", labelcolor=plot_color) # バーグラフ(右Y軸) ax2 = ax1.twinx() ax2.bar(df[time_column_name], df[bar_column_name], color=bar_color, alpha=0.3, label=bar_column_name, width=20) ax2.set_ylabel(bar_column_name, color=bar_color) ax2.tick_params(axis="y", labelcolor=bar_color) # タイトルの設定 plt.title(title) # 凡例 fig.tight_layout() ax1.legend(loc="upper left") ax2.legend(loc="upper right") # グラフの表示 plt.show()
実行例
手順1:必要なライブラリのインポート
import pandas as pd import numpy as np import matplotlib.pyplot as plt
手順2:サンプルデータセットの作成
- 以下のコードでサンプルデータを作成できます!
- 今回の内容は、小売を想定したサンプルデータセットになります。
order_date:販売日store_id:店舗IDproduct_id:商品IDproduct_price:販売価格quantity:販売数量total_sales:合計販売金額
- 今回の内容は、小売を想定したサンプルデータセットになります。
# パラメータ設定 np.random.seed(42) dates = pd.date_range(start="2022-01-01", end="2024-12-31", freq="D") store_ids = range(1, 6) # 店舗ID 1~5 product_ids = range(101, 111) # 商品ID 101~110 # 商品ごとの販売価格をランダムに設定(100~500円) product_prices = {pid: np.random.randint(100, 501) for pid in product_ids} # データ生成 data = [] for date in dates: for store_id in store_ids: for product_id in product_ids: quantity = np.random.randint(0, 21) # 販売数量(0~20) if quantity > 0: # 売れた場合のみ記録 data.append([date, store_id, product_id, product_prices[product_id], quantity, product_prices[product_id] * quantity])
手順3:月間合計販売金額、月間合計販売数量の算出
- 今回は横軸:
年月、プロット:(月間合計販売金額)、バー:(月間合計販売数量)を描写していきたいと思います。- 集計対象カラム
order_date:販売日total_sales:販売金額quantity:販売数量
- 集計対象カラム
- そのため、pandasで以下のように集計し、グラフ描写用のデータフレームを作成する。
# 年月に変換 df["year_month"] = df["order_date"].dt.to_period("M") # 年月単位で販売合計金額と販売合計数量を集計 df_monthly_summary = df.groupby("year_month").agg( total_sales=("total_sales", "sum"), total_quantity=("quantity", "sum") ).reset_index() # 'year_month'列をPeriod型からdatetime型に変換 df_monthly_summary['year_month'] = df_monthly_summary['year_month'].dt.to_timestamp()
手順4:今回の目的の関数の定義
def plot_and_bar_graph(...)
手順5:プロット&バーグラフの描写
plot_and_bar_graph(
df=df_monthly_summary,
time_column_name="year_month",
plot_column_name="total_sales",
bar_column_name="total_quantity",
plot_color="blue",
bar_color="orange",
title="Monthly Sales and Quantity",
figsize=(10, 6)
)

皆さんの分析の効率化の一助になれば幸いです!!
【GeoPandas】表形式データから地図上に情報を可視化する方法
実店舗系のデータ解析に携わっているPDチーム/データサイエンティストの川上雄大です!
以前執筆した記事で,業務で利用した全国道路・街路交通情勢調査一般交通量調査(道路交通センサス:RTC)情報を国土交通省データプラットフォームのAPIで取得する方法を紹介ました.RTCデータは緯度経度に合わせて24時間自動車類交通量合計(metadata.RTC:24jikan_jidousharui_koutsuuryou_jouge_goukei_goukei)のような道路交通量を代表するような値を取得できます.以下の図に東京都に関して取得したRTCデータを示します.

この表からわかる通り,表形式データでは,土地的な情報を理解することは難しいです.
そこで,本記事では,GeoPandasを使用して,道路交通センサス情報を整理し,地図上に可視化する方法を解説します.GeoPandasを使用することで,地理情報データを扱うための機能を提供し,地理情報データの可視化や解析を行う際に便利です.
特に今回は,表形式データで示された緯度経度上の交通量を地図上のヒートマップで可視化する例を紹介します!
本記事では,GeoPandasの説明,データ処理方法の順で解説を行います.
1. はじめに
まず,本節では,GeoPandasの概要について説明します.
GeoPandas とは
GeoPandasは,Pythonでジオスペーシャルデータを扱うためのライブラリです.GeoPandasは,pandasのデータフレームを拡張し,ジオメトリデータを扱うための機能を提供します.GeoPandasを使用すると,地理情報システム(GIS)のような専用のツールを使用せずに,Pythonでジオスペーシャルデータを簡単に操作できます.
GeoPandasは,地理情報データを扱うための機能を提供するため,地理情報データの可視化や解析を行う際に便利です.
2. データ処理方法
本節では,GeoPandasを使用して,取得した道路交通センサス情報を整理し,地図上に可視化する方法を解説します.
2.1. GeoPandasのインストール
まずはじめに,GeoPandasに関連するライブラリをインストールします.
pip install geopandas contextily
2.2. データの変換
まずは以前執筆した記事を参考に,東京都の2021年度のRTCデータを取得します.ここでは,DPF:prefecture_codeを13(東京都)に設定して,東京都のRTCデータを取得します.前回の記事と重複するため,詳細は省略しますが,df_rtcという変数にDataFrame形式でデータを取得します.
まずは,GeoPandasのデータフレームに変換します.
import geopandas as gpd from shapely.geometry import Point # 地理データフレームに変換 geometry = [Point(xy) for xy in zip(df_rtc['metadata.DPF:longitude'], df_rtc['metadata.DPF:latitude'])] gdf = gpd.GeoDataFrame( df_rtc[[ "metadata.RTC:24jikan_jidousharui_koutsuuryou_jouge_goukei_goukei", # 24時間自動車類交通量(上下合計) 合計(台) ]], geometry=geometry, crs='EPSG:4326', ) gdf = gdf.to_crs(epsg=3099) gdf
一見すると,通常のDataFrameと同じように見えますが,次のようなGeoDataFrameになっています.

ここで,緯度経度をPoint型に変換し,GeoDataFrameに変換しています.また,crs='EPSG:4326'で緯度経度を示す座標系を指定し,to_crs(epsg=3099)で日本の平面直角座標系に変換しています.平面座標系に変換することで,地図上にデータをプロットする際に,正確な距離関係の下で位置にプロットすることができます.
今回は,このままだとデータ数が多すぎるため,交通量が50000以上の主要道路のみを抽出します.
gdf_major = gdf[gdf['metadata.RTC:24jikan_jidousharui_koutsuuryou_jouge_goukei_goukei'] > 50000]
2.3. 地図上に可視化
最後に,地図上にデーカを可視化します.
import contextily as ctx import matplotlib.pyplot as plt import matplotlib.colors as mcolors # カラーマップの設定 cmap = plt.get_cmap('coolwarm') # プロットの作成 fig, ax = plt.subplots(figsize=(10, 10)) gdf_major.plot( ax=ax, column='metadata.RTC:24jikan_jidousharui_koutsuuryou_jouge_goukei_goukei', cmap=cmap, markersize=20, marker='o', legend=False, alpha=0.8, ) # カラーバーの追加 sm = plt.cm.ScalarMappable(cmap=cmap) sm.set_array(gdf_major['metadata.RTC:24jikan_jidousharui_koutsuuryou_jouge_goukei_goukei']) cbar = plt.colorbar(sm, ax=ax) cbar.set_label('Traffic Volume') # 背景地図を追加 ctx.add_basemap(ax, crs=gdf.crs.to_string(), alpha=0.4) # タイトルとラベルの設定 plt.xlabel('Easting (m)') plt.ylabel('Northing (m)') # 表示 plt.show()
このコードを実行することで,次のように地図上に交通量が50000以上の主要道路のデータがプロットされます.

東京都では,湾岸部と中央部に交通量が多い道路が特に集中していることが確認できました!
3. まとめ
本記事では,GeoPandasを使用して,取得した道路交通センサス情報を整理し,地図上に可視化する方法を解説しました.GeoPandasを使用することで,地理情報データを扱うための機能を提供し,地理情報データの可視化や解析を行う際に便利です.
今回の記事を参考にして,GeoPandasを使用して,道路交通センサス情報を取得し,地図上に可視化してみてください!
参考文献
| GeoPandas | https://geopandas.org/en/stable/ |
データ分析で汎用的に使えるPython入門1:ヒストグラム, 度数, 累積度数, 累積度数割合
SENSYデータサイエンティストチーム井上です。
今回はデータ分析で汎用的に使えるPythonの記事(第1回目)になります。 EDA(Explanatory Data Analysis, 探索的データ分析)の際、よく確認する内容を関数化しておくことで作業を効率化するシリーズになります!!
今回の目的
要点
- ヒストグラム, 累積度数, 累積度数割合を出力する汎用的な関数を作成し、作業を効率化する
今回の関数で作成されるもの
- ヒストグラムグラフ
- 度数, 累積度数, 累積度数割合表
今回の目的の関数
def plot_histogram_and_create_frequency_table(df, target_column_name, bins=None, xlabel="", ylabel="Frequency", title="histogram", color="skyblue", figsize=(10,6)): """ 1. Pandas DataFrameを入力に、指定した列のヒストグラムを計算し、グラフを描写する 2. 度数、累積度数、累積割合を含むデータフレームを返す Args: df (pd.DataFrame): 入力のデータフレーム target_column_name (str): 計算対象の列名 bins (list or np.ndarray, optional): ビンの境界を指定するリストまたはNumPy配列, Noneの場合は自動的にビンを計算 xlabel (str): 横軸のラベル名 ylabel (str): 縦軸のラベル名 title (str): グラフのタイトル名 color (str): バーの色(デフォルトは"skyblue") figsize (tuple): 図のサイズ Returns: result_df (pd.DataFrame): 度数、累積度数、累積割合を含むデータフレーム """ # ビンの自動設定 if bins is None: bins = np.histogram_bin_edges(df[target_column_name], bins='auto') # ヒストグラムの計算 hist, edges = np.histogram(df[target_column_name], bins=bins) # 結果をデータフレームに整理 result_df = pd.DataFrame({ 'Bin': pd.IntervalIndex.from_breaks(edges, closed='left'), # ビンの範囲 'Frequency': hist, # 度数 }) result_df['Cumulative Frequency'] = result_df['Frequency'].cumsum() # 累積度数 result_df['Cumulative Percentage'] = (result_df['Cumulative Frequency'] / result_df['Frequency'].sum()) * 100 # 累積割合 # ヒストグラムの描画 plt.figure(figsize=figsize) plt.bar(edges[:-1], hist, width=np.diff(edges), align='edge', color=color, edgecolor='black', alpha=0.7) plt.xlabel(xlabel) plt.ylabel(ylabel) plt.title(title) plt.grid(axis='y', alpha=0.75) plt.show() return result_df
おまけ
def dataframe_unlimited_display_in_notebook_cell(df) -> None: """ノートブックのこの関数を実行するセル内でのデータフレームの表示を無制限にする Args: df (pd.DataFrame): データフレーム """ with pd.option_context('display.max_rows', None, 'display.max_columns', None): display(df)
実行例
手順1:ライブラリ&データセットの準備
- 今回は、
scikit-learnの公開データセット「california housing dataset(カリフォルニアの住宅価格データセット)」を利用します。
import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import fetch_california_housing california_housing_data = fetch_california_housing()
手順2:データセットをデータフレームへ落とし込む
df = pd.concat([
pd.DataFrame(california_housing_data.data, columns=california_housing_data.feature_names), # 説明変数
pd.Series(california_housing_data.target, name=california_housing_data.target_names[0]) # 目的変数
], axis=1)
手順3:今回の目的の関数の定義
def plot_histogram_and_create_frequency_table(...)
手順4:ヒストグラムの描写
- 今回は目的変数
MedHouseValを描写していきたいと思います!
binsを自動設定にした場合
result_df = plot_histogram_and_create_frequency_table(
df=df,
target_column_name="MedHouseVal",
bins=None,
xlabel="MedHouseVal",
ylabel="Frequency",
title="Histogram (California Housing dataset)",
color="skyblue",
figsize=(10,6)
)

binsを手動設定にした場合
result_df = plot_histogram_and_create_frequency_table(
df=df,
target_column_name="MedHouseVal",
bins=np.arange(0, 5.3, 0.2),
xlabel="MedHouseVal",
ylabel="Frequency",
title="Histogram (California Housing dataset)",
color="skyblue",
figsize=(10,6)
)

手順5:度数, 累積度数, 累積度数割合表の描写
dataframe_unlimited_display_in_notebook_cell(result_df)
binsを自動設定にした場合
出力結果(長いので折りたたみ)
| Bin | Frequency | Cumulative Frequency | Cumulative Percentage | |
|---|---|---|---|---|
| 0 | [0.14999, 0.25542521739130436) | 10 | 10 | 0.0484496 |
| 1 | [0.25542521739130436, 0.3608604347826087) | 16 | 26 | 0.125969 |
| 2 | [0.3608604347826087, 0.46629565217391306) | 88 | 114 | 0.552326 |
| 3 | [0.46629565217391306, 0.5717308695652175) | 407 | 521 | 2.52422 |
| 4 | [0.5717308695652175, 0.6771660869565217) | 636 | 1157 | 5.60562 |
| 5 | [0.6771660869565217, 0.7826013043478262) | 661 | 1818 | 8.80814 |
| 6 | [0.7826013043478262, 0.8880365217391304) | 777 | 2595 | 12.5727 |
| 7 | [0.8880365217391304, 0.9934717391304348) | 966 | 3561 | 17.2529 |
| 8 | [0.9934717391304348, 1.098906956521739) | 763 | 4324 | 20.9496 |
| 9 | [1.098906956521739, 1.2043421739130435) | 890 | 5214 | 25.2616 |
| 10 | [1.2043421739130435, 1.3097773913043478) | 761 | 5975 | 28.9486 |
| 11 | [1.3097773913043478, 1.4152126086956522) | 915 | 6890 | 33.3818 |
| 12 | [1.4152126086956522, 1.5206478260869567) | 886 | 7776 | 37.6744 |
| 13 | [1.5206478260869567, 1.626083043478261) | 1119 | 8895 | 43.0959 |
| 14 | [1.626083043478261, 1.7315182608695652) | 888 | 9783 | 47.3983 |
| 15 | [1.7315182608695652, 1.8369534782608696) | 852 | 10635 | 51.5262 |
| 16 | [1.8369534782608696, 1.942388695652174) | 904 | 11539 | 55.906 |
| 17 | [1.942388695652174, 2.0478239130434783) | 610 | 12149 | 58.8614 |
| 18 | [2.0478239130434783, 2.1532591304347823) | 604 | 12753 | 61.7878 |
| 19 | [2.1532591304347823, 2.258694347826087) | 736 | 13489 | 65.3537 |
| 20 | [2.258694347826087, 2.3641295652173913) | 631 | 14120 | 68.4109 |
| 21 | [2.3641295652173913, 2.4695647826086953) | 567 | 14687 | 71.1579 |
| 22 | [2.4695647826086953, 2.5749999999999997) | 471 | 15158 | 73.4399 |
| 23 | [2.5749999999999997, 2.680435217391304) | 478 | 15636 | 75.7558 |
| 24 | [2.680435217391304, 2.7858704347826087) | 480 | 16116 | 78.0814 |
| 25 | [2.7858704347826087, 2.891305652173913) | 367 | 16483 | 79.8595 |
| 26 | [2.891305652173913, 2.996740869565217) | 290 | 16773 | 81.2645 |
| 27 | [2.996740869565217, 3.1021760869565216) | 242 | 17015 | 82.437 |
| 28 | [3.1021760869565216, 3.207611304347826) | 247 | 17262 | 83.6337 |
| 29 | [3.207611304347826, 3.31304652173913) | 265 | 17527 | 84.9176 |
| 30 | [3.31304652173913, 3.4184817391304345) | 281 | 17808 | 86.2791 |
| 31 | [3.4184817391304345, 3.523916956521739) | 306 | 18114 | 87.7616 |
| 32 | [3.523916956521739, 3.6293521739130434) | 226 | 18340 | 88.8566 |
| 33 | [3.6293521739130434, 3.734787391304348) | 173 | 18513 | 89.6948 |
| 34 | [3.734787391304348, 3.840222608695652) | 160 | 18673 | 90.47 |
| 35 | [3.840222608695652, 3.9456578260869564) | 134 | 18807 | 91.1192 |
| 36 | [3.9456578260869564, 4.051093043478261) | 129 | 18936 | 91.7442 |
| 37 | [4.051093043478261, 4.156528260869565) | 111 | 19047 | 92.282 |
| 38 | [4.156528260869565, 4.26196347826087) | 109 | 19156 | 92.8101 |
| 39 | [4.26196347826087, 4.367398695652174) | 94 | 19250 | 93.2655 |
| 40 | [4.367398695652174, 4.472833913043478) | 90 | 19340 | 93.7016 |
| 41 | [4.472833913043478, 4.578269130434783) | 104 | 19444 | 94.2054 |
| 42 | [4.578269130434783, 4.683704347826087) | 53 | 19497 | 94.4622 |
| 43 | [4.683704347826087, 4.789139565217391) | 61 | 19558 | 94.7578 |
| 44 | [4.789139565217391, 4.894574782608696) | 48 | 19606 | 94.9903 |
| 45 | [4.894574782608696, 5.00001) | 1034 | 20640 | 100 |
binsを手動設定にした場合
出力結果(長いので折りたたみ)
| Bin | Frequency | Cumulative Frequency | Cumulative Percentage | |
|---|---|---|---|---|
| 0 | [0.0, 0.2) | 5 | 5 | 0.0242248 |
| 1 | [0.2, 0.4) | 35 | 40 | 0.193798 |
| 2 | [0.4, 0.6) | 641 | 681 | 3.29942 |
| 3 | [0.6, 0.8) | 1227 | 1908 | 9.24419 |
| 4 | [0.8, 1.0) | 1688 | 3596 | 17.4225 |
| 5 | [1.0, 1.2) | 1581 | 5177 | 25.0824 |
| 6 | [1.2, 1.4) | 1577 | 6754 | 32.7229 |
| 7 | [1.4, 1.6) | 1772 | 8526 | 41.3081 |
| 8 | [1.6, 1.8) | 1812 | 10338 | 50.0872 |
| 9 | [1.8, 2.0) | 1547 | 11885 | 57.5824 |
| 10 | [2.0, 2.2) | 1176 | 13061 | 63.28 |
| 11 | [2.2, 2.4) | 1256 | 14317 | 69.3653 |
| 12 | [2.4, 2.6) | 942 | 15259 | 73.9293 |
| 13 | [2.6, 2.8) | 915 | 16174 | 78.3624 |
| 14 | [2.8, 3.0) | 600 | 16774 | 81.2694 |
| 15 | [3.0, 3.2) | 466 | 17240 | 83.5271 |
| 16 | [3.2, 3.4) | 506 | 17746 | 85.9787 |
| 17 | [3.4, 3.6) | 523 | 18269 | 88.5126 |
| 18 | [3.6, 3.8) | 345 | 18614 | 90.1841 |
| 19 | [3.8, 4.0) | 255 | 18869 | 91.4196 |
| 20 | [4.0, 4.2) | 226 | 19095 | 92.5145 |
| 21 | [4.2, 4.4) | 182 | 19277 | 93.3963 |
| 22 | [4.4, 4.6) | 178 | 19455 | 94.2587 |
| 23 | [4.6, 4.8) | 106 | 19561 | 94.7723 |
| 24 | [4.8, 5.0) | 87 | 19648 | 95.1938 |
| 25 | [5.0, 5.2) | 992 | 20640 | 100 |
皆さんの分析の効率化の一助になれば幸いです!!
gcloudコマンドでカンマをエスケープ
SENSYプロダクトチーム藤沼です
はじめに
SENSY CLOUDでは、GCPのPub/Subを活用してシステムアーキテクチャを構成しています。本記事では、Pub/Subを用いた技術検証における便利なヒントを共有します。
やりたいこと
Pub/Subの技術検証を行う際、手元でgcloud CLIを使い、メッセージをパブリッシュして動作確認を行いたいことがあります。特に、attributesに複数の情報をカンマ区切りで渡したい場面がよくあります。
たとえば、attributesのキーtable_nameに対し、以下の3つのテーブル名を指定したい場合を考えます。
sales_transactionstore_sku_masterstore_master
ダメな指定方法
以下のように指定すると、一見すると正しそうに見えますが問題があります。
gcloud pubsub topics publish sample-topic \
--attribute=project=<project>,table_name=sales_transaction,store_sku_master,store_master,location=us-central1
このコマンドでは、どの部分がどのように区切られているのかが曖昧で、意図通りに動作しません。実行すると以下のようなエラーメッセージが表示されます。
ERROR: (gcloud.pubsub.topics.publish) argument --attribute: Bad syntax for dict arg: [list1]. Please see `gcloud topic flags-file` or `gcloud topic escaping` for information on providing list or dictionary flag values with special characters.
エスケープ方法
エラーメッセージに記載されている公式ドキュメントを参考に、正しい指定方法に修正します。
以下のように、--attribute=の最初に^:^を付けることで解決できます。これにより、属性間の区切り文字をデフォルトのカンマからコロン(:)に変更することができます。
gcloud pubsub topics publish sample-topic \
--attribute=^:^project=<project>:table_name=sales_transaction,store_sku_master,store_master:location=us-central1
この指定方法を使うと、途中のカンマが区切り文字と認識されることがなくなり、table_nameキーには"sales_transaction,store_sku_master,store_master"の文字列が正しく渡されます。
最後に
SENSY / プロダクト開発チームではSENSY CLOUDを一緒に開発するデータサイエンティスト・機械学習エンジニアを随時募集しています。