だから僕はpandasを辞めた【データサイエンス100本ノック(構造化データ加工編)篇 #2】
データサイエンス100本ノック(構造化データ加工編)のPythonの問題を解いていきます。この問題群は、模範解答ではpandasを使ってデータ加工を行っていますが、私達は勉強がてらにNumPyの構造化配列を用いて処理していきます。
はじめに
NumPyの構造化配列の勉強として、データサイエンス100本ノック(構造化データ加工編)のPythonの問題を解いていきます。
Pythonでデータサイエンス的なことをする人の多くはpandas大好き人間かもしれませんが、実はpandasを使わなくても、NumPyで同じことができます。そしてNumPyの方がたいてい高速です。
pandas大好き人間だった僕もNumPyの操作には依然として慣れていないので、今回この『データサイエンス100本ノック』をNumPyで操作することでpandasからの卒業を試みて行きたいと思います。
なおnp.vectorize()
やnp.frompyfunc()
等による関数のベクトル化は行わない方針です。
今回は10~16問目をやっていきます。文字列の条件インデックスというテーマのようです。
初期データは以下のようにして読み込みました(データ型指定はとりあえず後回し)。
import numpy as np
import pandas as pd
# 模範解答用
df_store = pd.read_csv('data/store.csv')
df_customer = pd.read_csv('data/customer.csv')
# 僕たちが扱うデータ
arr_store = np.genfromtxt(
'data/store.csv', delimiter=',', encoding='utf-8',
names=True, dtype=None)
arr_customer = np.genfromtxt(
'data/customer.csv', delimiter=',', encoding='utf-8',
names=True, dtype=None)
P_010
P-010: 店舗データフレーム(df_store)から、店舗コード(store_cd)が"S14"で始まるものだけ全項目抽出し、10件だけ表示せよ。
文字列の先頭が一致するかどうかはnp.char.startswith()
を用います。第一引数に文字列の配列、第二引数に検索するワードを与えます。
arr_store[np.char.startswith(arr_store['store_cd'], 'S14')][:10]
np.char.xxx()
のような関数を使うのは簡単ですが、NumPyにおいて文字列関連の操作は苦手分野なので、速度を求める場合はPython標準のforループを使う方が良いことがしばしばあります。その場合NumPy配列をわざわざリストに変換すると速くなります。
arr_store[[item[:3] == 'S14'
for item in arr_store['store_cd'].tolist()]][:10]
先頭の数文字だけを見る場合は、より簡単かつ高速に処理する方法が存在します。店舗コード(store_cd)列を確認すると、
arr_store['store_cd']
# array(['S12014', 'S13002', 'S14010', 'S14033', 'S14036', 'S13051',
# ...
# 'S13003', 'S12053', 'S13037', 'S14024', 'S14006'], dtype='<U6')
すべて6桁の文字列で構成されていることがわかります。必要なのは先頭3文字だけなので、これをU3
データ型で読み直します。すると、4文字目以降が読み取られず捨てられて以下のようになります。
arr_store['store_cd'].astype('<U3')
# array(['S12', 'S13', 'S14', 'S14', 'S14', 'S13', 'S13', 'S14', 'S13',
# ...
# 'S14', 'S13', 'S12', 'S13', 'S12', 'S13', 'S14', 'S14'],
# dtype='<U3')
これが“S14”のものを引っこ抜けばいいので、解答は以下のようになります。
arr_store[arr_store['store_cd'].astype('<U3') == 'S14'][:10]
出力は以下の通り。
array([('S14010', '菊名店', 14, '神奈川県', '神奈川県横浜市港北区菊名一丁目', 'カナガワケンヨコハマシコウホククキクナイッチョウメ', '045-123-4032', 139.6326, 35.50049, 1732.),
('S14033', '阿久和店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4043', 139.4961, 35.45918, 1495.),
('S14036', '相模原中央店', 14, '神奈川県', '神奈川県相模原市中央二丁目', 'カナガワケンサガミハラシチュウオウニチョウメ', '042-123-4045', 139.3716, 35.57327, 1679.),
('S14040', '長津田店', 14, '神奈川県', '神奈川県横浜市緑区長津田みなみ台五丁目', 'カナガワケンヨコハマシミドリクナガツタミナミダイゴチョウメ', '045-123-4046', 139.4994, 35.52398, 1548.),
('S14050', '阿久和西店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4053', 139.4961, 35.45918, 1830.),
('S14028', '二ツ橋店', 14, '神奈川県', '神奈川県横浜市瀬谷区二ツ橋町', 'カナガワケンヨコハマシセヤクフタツバシチョウ', '045-123-4042', 139.4963, 35.46304, 1574.),
('S14012', '本牧和田店', 14, '神奈川県', '神奈川県横浜市中区本牧和田', 'カナガワケンヨコハマシナカクホンモクワダ', '045-123-4034', 139.6582, 35.42156, 1341.),
('S14046', '北山田店', 14, '神奈川県', '神奈川県横浜市都筑区北山田一丁目', 'カナガワケンヨコハマシツヅキクキタヤマタイッチョウメ', '045-123-4049', 139.5916, 35.56189, 831.),
('S14022', '逗子店', 14, '神奈川県', '神奈川県逗子市逗子一丁目', 'カナガワケンズシシズシイッチョウメ', '046-123-4036', 139.5789, 35.29642, 1838.),
('S14011', '日吉本町店', 14, '神奈川県', '神奈川県横浜市港北区日吉本町四丁目', 'カナガワケンヨコハマシコウホククヒヨシホンチョウヨンチョウメ', '045-123-4033', 139.6316, 35.54655, 890.)],
dtype=[('store_cd', '<U6'), ('store_name', '<U6'), ('prefecture_cd', '<i4'), ('prefecture', '<U4'), ('address', '<U19'), ('address_kana', '<U30'), ('tel_no', '<U12'), ('longitude', '<f8'), ('latitude', '<f8'), ('floor_area', '<f8')])
色んな方法で、それぞれ速度を比較してみます。
# 模範解答
%timeit df_store.query("store_cd.str.startswith('S14')", engine='python').head(10)
# 3.46 ms ± 23.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# pandasのほかの方法
%timeit df_store[df_store['store_cd'].str.startswith('S14')][:10]
# 876 µs ± 18.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit df_store.loc[[index for index, item in enumerate(df_store['store_cd']) if item[:3] == 'S14']][:10]
# 732 µs ± 17.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# NumPyを使った方法
%timeit arr_store[np.char.startswith(arr_store['store_cd'], 'S14')][:10]
# 54.3 µs ± 3.17 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit arr_store[[item[:3] == 'S14' for item in arr_store['store_cd'].tolist()]][:10]
# 22.8 µs ± 377 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit arr_store[arr_store['store_cd'].astype('<U3') == 'S14'][:10]
# 5.55 µs ± 91.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
pd.DataFrame.query()
を使った文字列条件インデックスはおそいことで有名です。
P_011
P-011: 顧客データフレーム(df_customer)から顧客ID(customer_id)の末尾が1のものだけ全項目抽出し、10件だけ表示せよ。
前問と同じですね。np.char.endswith()
関数を使います。
arr_customer[np.char.endswith(arr_customer['customer_id'], '1')][:10]
速度が気になる場合はforループ。
arr_customer[[item[-1] == '1'
for item in arr_customer['customer_id']]][:10]
より速い方法を考えます。
文字列の最後の字を見る場合は.astype()
戦法は使えません。しかし、今回の例では、また別の方法で高速処理することが可能です。顧客ID(customer_id)列を確認すると、
arr_customer['customer_id']
# array(['CS021313000114', 'CS037613000071', 'CS031415000172', ...,
# 'CS012403000043', 'CS033512000184', 'CS009213000022'], dtype='<U14')
すべての行が同じ文字数(14桁)になっていることがわかります。この最後が“1”のものを探せばいいわけです。
これを実行するには、まず配列arr_customer['customer_id']
を.tobytes()
メソッドを用いてバイト列に変換し、データ型'U14'
で読まれていたこの配列をnp.frombuffer()
関数を用いて'U1'
データ型で読み直します。それをさらにreshape()
メソッドで14列の配列に組み替えると、以下のようになります。
# 顧客IDの全データを1文字ずつに分解
np.frombuffer(arr_customer['customer_id'].tobytes(), dtype='<U1')
# array(['C', 'S', '0', ..., '0', '2', '2'], dtype='<U1')
# 1行14文字ずつに戻す
np.frombuffer(arr_customer['customer_id'].tobytes(), dtype='<U1').reshape(len(arr_customer), -1)
# array([['C', 'S', '0', ..., '1', '1', '4'],
# ['C', 'S', '0', ..., '0', '7', '1'],
# ...,
# ['C', 'S', '0', ..., '1', '8', '4'],
# ['C', 'S', '0', ..., '0', '2', '2']], dtype='<U1')
この配列の「最後の列が“1”の行」が求める行なので、以下のようになります。この手法はかなり応用がききます。
arr_customer[np.frombuffer(arr_customer['customer_id'].tobytes(), dtype='<U1')
.reshape(len(arr_customer), -1)[:, -1]
== '1'][:10]
出力は以下の通りです。
array([('CS037613000071', '六角 雅彦', 9, '不明', '1952-04-01', 66, '136-0076', '東京都江東区南砂**********', 'S13037', 20150414, '0-00000000-0'),
('CS028811000001', '堀井 かおり', 1, '女性', '1933-03-27', 86, '245-0016', '神奈川県横浜市泉区和泉町**********', 'S14028', 20160115, '0-00000000-0'),
('CS040412000191', '川井 郁恵', 1, '女性', '1977-01-05', 42, '226-0021', '神奈川県横浜市緑区北八朔町**********', 'S14040', 20151101, '1-20091025-4'),
('CS028314000011', '小菅 あおい', 1, '女性', '1983-11-26', 35, '246-0038', '神奈川県横浜市瀬谷区宮沢**********', 'S14028', 20151123, '1-20080426-5'),
('CS039212000051', '藤島 恵梨香', 1, '女性', '1997-02-03', 22, '166-0001', '東京都杉並区阿佐谷北**********', 'S13039', 20171121, '1-20100215-4'),
('CS015412000111', '松居 奈月', 1, '女性', '1972-10-04', 46, '136-0071', '東京都江東区亀戸**********', 'S13015', 20150629, '0-00000000-0'),
('CS004702000041', '野島 洋', 0, '男性', '1943-08-24', 75, '176-0022', '東京都練馬区向山**********', 'S13004', 20170218, '0-00000000-0'),
('CS041515000001', '栗田 千夏', 1, '女性', '1967-01-02', 52, '206-0001', '東京都多摩市和田**********', 'S13041', 20160422, 'E-20100803-F'),
('CS029313000221', '北条 ひかり', 1, '女性', '1987-06-19', 31, '279-0011', '千葉県浦安市美浜**********', 'S12029', 20180810, '0-00000000-0'),
('CS034312000071', '望月 奈央', 1, '女性', '1980-09-20', 38, '213-0026', '神奈川県川崎市高津区久末**********', 'S14034', 20160106, '0-00000000-0')],
dtype=[('customer_id', '<U14'), ('customer_name', '<U10'), ('gender_cd', '<i4'), ('gender', '<U2'), ('birth_day', '<U10'), ('age', '<i4'), ('postal_cd', '<U8'), ('address', '<U26'), ('application_store_cd', '<U6'), ('application_date', '<i4'), ('status_cd', '<U12')])
速度を比較してみます。
%timeit df_customer.query("customer_id.str.endswith('1')", engine='python').head(10)
# 12.2 ms ± 454 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit arr_customer[np.char.endswith(arr_customer['customer_id'], '1')][:10]
# 20.7 ms ± 847 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit arr_customer[[item[-1] == '1' for item in arr_customer['customer_id']]][:10]
# 9.44 ms ± 185 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit arr_customer[np.frombuffer(arr_customer['customer_id'].tobytes(), dtype='<U1').reshape(len(arr_customer), -1)[:, -1] == '1'][:10]
# 1.83 ms ± 77 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
np.char.xxx()
は場合によってはpd.query()
よりも遅いようです……。
P_012
P-012: 店舗データフレーム(df_store)から横浜市の店舗だけ全項目表示せよ。
arr_store[np.char.find(arr_store['address'], '横浜市') >= 0]
または、
arr_store[['横浜市' in item
for item in arr_store['address'].tolist()]]
住所欄は文字数が不定ですが「横浜市」は必ず5・6・7文字目に現れるので、.astype('<U7')
で先頭7文字だけに切ったあとで最後の3文字だけの配列を作って、
arr_store[np.frombuffer(arr_store['address'].astype('<U7').view('<U1')
.reshape(len(arr_store), -1)[:, 4:].tobytes(),
dtype='<U3')
== '横浜市']
とすることもできます。
array([('S14010', '菊名店', 14, '神奈川県', '神奈川県横浜市港北区菊名一丁目', 'カナガワケンヨコハマシコウホククキクナイッチョウメ', '045-123-4032', 139.6326, 35.50049, 1732.),
('S14033', '阿久和店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4043', 139.4961, 35.45918, 1495.),
('S14040', '長津田店', 14, '神奈川県', '神奈川県横浜市緑区長津田みなみ台五丁目', 'カナガワケンヨコハマシミドリクナガツタミナミダイゴチョウメ', '045-123-4046', 139.4994, 35.52398, 1548.),
('S14050', '阿久和西店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4053', 139.4961, 35.45918, 1830.),
('S14028', '二ツ橋店', 14, '神奈川県', '神奈川県横浜市瀬谷区二ツ橋町', 'カナガワケンヨコハマシセヤクフタツバシチョウ', '045-123-4042', 139.4963, 35.46304, 1574.),
('S14012', '本牧和田店', 14, '神奈川県', '神奈川県横浜市中区本牧和田', 'カナガワケンヨコハマシナカクホンモクワダ', '045-123-4034', 139.6582, 35.42156, 1341.),
('S14046', '北山田店', 14, '神奈川県', '神奈川県横浜市都筑区北山田一丁目', 'カナガワケンヨコハマシツヅキクキタヤマタイッチョウメ', '045-123-4049', 139.5916, 35.56189, 831.),
('S14011', '日吉本町店', 14, '神奈川県', '神奈川県横浜市港北区日吉本町四丁目', 'カナガワケンヨコハマシコウホククヒヨシホンチョウヨンチョウメ', '045-123-4033', 139.6316, 35.54655, 890.),
('S14048', '中川中央店', 14, '神奈川県', '神奈川県横浜市都筑区中川中央二丁目', 'カナガワケンヨコハマシツヅキクナカガワチュウオウニチョウメ', '045-123-4051', 139.5758, 35.54912, 1657.),
('S14042', '新山下店', 14, '神奈川県', '神奈川県横浜市中区新山下二丁目', 'カナガワケンヨコハマシナカクシンヤマシタニチョウメ', '045-123-4047', 139.6593, 35.43894, 1044.),
('S14006', '葛が谷店', 14, '神奈川県', '神奈川県横浜市都筑区葛が谷', 'カナガワケンヨコハマシツヅキククズガヤ', '045-123-4031', 139.5633, 35.53573, 1886.)],
dtype=[('store_cd', '<U6'), ('store_name', '<U6'), ('prefecture_cd', '<i4'), ('prefecture', '<U4'), ('address', '<U19'), ('address_kana', '<U30'), ('tel_no', '<U12'), ('longitude', '<f8'), ('latitude', '<f8'), ('floor_area', '<f8')])
P_013
P-013: 顧客データフレーム(df_customer)から、ステータスコード(status_cd)の先頭がアルファベットのA〜Fで始まるデータを全項目抽出し、10件だけ表示せよ。
こうなると面倒です。模範解答では正規表現で検索していますが、とりあえず正規表現を使わずやっていきます。
np.char.startswith()
で行くならこうなります。
arr_customer[
np.char.startswith(arr_customer['status_cd'],
np.array(list('ABCDEF'))[:, None])
.any(0)][:10]
相当しんどい感じがします。forを使ったほうが簡単です。
arr_customer[[item[0] in set('ABCDEF')
for item in arr_customer['status_cd'].tolist()]][:10]
文字列の先頭だけ読み込んでnp.in1d()
。速いし簡単です。
arr_customer[np.in1d(arr_customer['status_cd'].astype('<U1'),
np.fromiter('ABCDEF', dtype='<U1'))][:10]
ちなみにここでは検索ワードもNumPy配列にしていますが、list('ABCDEF')
にしても速度はたいして変わりませんでした。
array([('CS031415000172', '宇多田 貴美子', 1, '女性', '1976-10-04', 42, '151-0053', '東京都渋谷区代々木**********', 'S13031', 20150529, 'D-20100325-C'),
('CS015414000103', '奥野 陽子', 1, '女性', '1977-08-09', 41, '136-0073', '東京都江東区北砂**********', 'S13015', 20150722, 'B-20100609-B'),
('CS011215000048', '芦田 沙耶', 1, '女性', '1992-02-01', 27, '223-0062', '神奈川県横浜市港北区日吉本町**********', 'S14011', 20150228, 'C-20100421-9'),
('CS029415000023', '梅田 里穂', 1, '女性', '1976-01-17', 43, '279-0043', '千葉県浦安市富士見**********', 'S12029', 20150610, 'D-20100918-E'),
('CS035415000029', '寺沢 真希', 9, '不明', '1977-09-27', 41, '158-0096', '東京都世田谷区玉川台**********', 'S13035', 20141220, 'F-20101029-F'),
('CS031415000106', '宇野 由美子', 1, '女性', '1970-02-26', 49, '151-0053', '東京都渋谷区代々木**********', 'S13031', 20150201, 'F-20100511-E'),
('CS029215000025', '石倉 美帆', 1, '女性', '1993-09-28', 25, '279-0022', '千葉県浦安市今川**********', 'S12029', 20150708, 'B-20100820-C'),
('CS033605000005', '猪股 雄太', 0, '男性', '1955-12-05', 63, '246-0031', '神奈川県横浜市瀬谷区瀬谷**********', 'S14033', 20150425, 'F-20100917-E'),
('CS033415000229', '板垣 菜々美', 1, '女性', '1977-11-07', 41, '246-0021', '神奈川県横浜市瀬谷区二ツ橋町**********', 'S14033', 20150712, 'F-20100326-E'),
('CS008415000145', '黒谷 麻緒', 1, '女性', '1977-06-27', 41, '157-0067', '東京都世田谷区喜多見**********', 'S13008', 20150829, 'F-20100622-F')],
dtype=[('customer_id', '<U14'), ('customer_name', '<U10'), ('gender_cd', '<i4'), ('gender', '<U2'), ('birth_day', '<U10'), ('age', '<i4'), ('postal_cd', '<U8'), ('address', '<U26'), ('application_store_cd', '<U6'), ('application_date', '<i4'), ('status_cd', '<U12')])
P_014
P-014: 顧客データフレーム(df_customer)から、ステータスコード(status_cd)の末尾が数字の1〜9で終わるデータを全項目抽出し、10件だけ表示せよ。
arr_customer[[item[-1] in set('123456789')
for item in arr_customer['status_cd'].tolist()]][:10]
arr_customer[np.in1d(np.frombuffer(arr_customer['status_cd'].tobytes(),
dtype='<U1')
.reshape(len(arr_customer), -1)[:, -1],
np.fromiter('123456789', dtype='<U1'))][:10]
array([('CS001215000145', '田崎 美紀', 1, '女性', '1995-03-29', 24, '144-0055', '東京都大田区仲六郷**********', 'S13001', 20170605, '6-20090929-2'),
('CS033513000180', '安斎 遥', 1, '女性', '1962-07-11', 56, '241-0823', '神奈川県横浜市旭区善部町**********', 'S14033', 20150728, '6-20080506-5'),
('CS011215000048', '芦田 沙耶', 1, '女性', '1992-02-01', 27, '223-0062', '神奈川県横浜市港北区日吉本町**********', 'S14011', 20150228, 'C-20100421-9'),
('CS040412000191', '川井 郁恵', 1, '女性', '1977-01-05', 42, '226-0021', '神奈川県横浜市緑区北八朔町**********', 'S14040', 20151101, '1-20091025-4'),
('CS009315000023', '皆川 文世', 1, '女性', '1980-04-15', 38, '154-0012', '東京都世田谷区駒沢**********', 'S13009', 20150319, '5-20080322-1'),
('CS015315000033', '福士 璃奈子', 1, '女性', '1983-03-17', 36, '135-0043', '東京都江東区塩浜**********', 'S13015', 20141024, '4-20080219-3'),
('CS023513000066', '神戸 そら', 1, '女性', '1961-12-17', 57, '210-0005', '神奈川県川崎市川崎区東田町**********', 'S14023', 20150915, '5-20100524-9'),
('CS035513000134', '市川 美帆', 1, '女性', '1960-03-27', 59, '156-0053', '東京都世田谷区桜**********', 'S13035', 20150227, '8-20100711-9'),
('CS001515000263', '高松 夏空', 1, '女性', '1962-11-09', 56, '144-0051', '東京都大田区西蒲田**********', 'S13001', 20160812, '1-20100804-1'),
('CS040314000027', '鶴田 きみまろ', 9, '不明', '1986-03-26', 33, '226-0027', '神奈川県横浜市緑区長津田**********', 'S14040', 20150122, '2-20080426-4')],
dtype=[('customer_id', '<U14'), ('customer_name', '<U10'), ('gender_cd', '<i4'), ('gender', '<U2'), ('birth_day', '<U10'), ('age', '<i4'), ('postal_cd', '<U8'), ('address', '<U26'), ('application_store_cd', '<U6'), ('application_date', '<i4'), ('status_cd', '<U12')])
P_015
P-015: 顧客データフレーム(df_customer)から、ステータスコード(status_cd)の先頭がアルファベットのA〜Fで始まり、末尾が数字の1〜9で終わるデータを全項目抽出し、10件だけ表示せよ。
条件が複数あってもforループで簡単に取得できます。
arr_customer[[item[0] in set('ABCDEF') and item[-1] in set('123456789')
for item in arr_customer['status_cd'].tolist()]][:10]
文字列を切ってnp.in1d()
を使う方法は条件が多くなり書くのが面倒になってきました。わざわざ以下のようなコードを書く人はいないでしょう。(もっといい方法がないだろうか……?)
statud_cd_split = np.frombuffer(arr_customer['status_cd'].tobytes(),
dtype='<U1').reshape(len(arr_customer), -1)
arr_customer[np.in1d(statud_cd_split[:, 0],
np.fromiter('ABCDEF', dtype='<U1'))
&
np.in1d(statud_cd_split[:, -1],
np.fromiter('123456789', dtype='<U1'))][:10]
array([('CS011215000048', '芦田 沙耶', 1, '女性', '1992-02-01', 27, '223-0062', '神奈川県横浜市港北区日吉本町**********', 'S14011', 20150228, 'C-20100421-9'),
('CS022513000105', '島村 貴美子', 1, '女性', '1962-03-12', 57, '249-0002', '神奈川県逗子市山の根**********', 'S14022', 20150320, 'A-20091115-7'),
('CS001515000096', '水野 陽子', 9, '不明', '1960-11-29', 58, '144-0053', '東京都大田区蒲田本町**********', 'S13001', 20150614, 'A-20100724-7'),
('CS013615000053', '西脇 季衣', 1, '女性', '1953-10-18', 65, '261-0026', '千葉県千葉市美浜区幕張西**********', 'S12013', 20150128, 'B-20100329-6'),
('CS020412000161', '小宮 薫', 1, '女性', '1974-05-21', 44, '174-0042', '東京都板橋区東坂下**********', 'S13020', 20150822, 'B-20081021-3'),
('CS001215000097', '竹中 あさみ', 1, '女性', '1990-07-25', 28, '146-0095', '東京都大田区多摩川**********', 'S13001', 20170315, 'A-20100211-2'),
('CS035212000007', '内村 恵梨香', 1, '女性', '1990-12-04', 28, '152-0023', '東京都目黒区八雲**********', 'S13035', 20151013, 'B-20101018-6'),
('CS002515000386', '野田 コウ', 1, '女性', '1963-05-30', 55, '185-0013', '東京都国分寺市西恋ケ窪**********', 'S13002', 20160410, 'C-20100127-8'),
('CS001615000372', '稲垣 寿々花', 1, '女性', '1956-10-29', 62, '144-0035', '東京都大田区南蒲田**********', 'S13001', 20170403, 'A-20100104-1'),
('CS032512000121', '松井 知世', 1, '女性', '1962-09-04', 56, '210-0011', '神奈川県川崎市川崎区富士見**********', 'S13032', 20150727, 'A-20100103-5')],
dtype=[('customer_id', '<U14'), ('customer_name', '<U10'), ('gender_cd', '<i4'), ('gender', '<U2'), ('birth_day', '<U10'), ('age', '<i4'), ('postal_cd', '<U8'), ('address', '<U26'), ('application_store_cd', '<U6'), ('application_date', '<i4'), ('status_cd', '<U12')])
書くのは面倒ですが、forループより高速です。
%timeit df_customer.query("status_cd.str.contains('^[A-F].*[1-9]$', regex=True)", engine='python').head(10)
# 31 ms ± 1.25 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit arr_customer[[item[0] in set('ABCDEF') and item[-1] in set('123456789') for item in arr_customer['status_cd'].tolist()]][:10]
# 16.7 ms ± 1.37 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
statud_cd_split = np.frombuffer(arr_customer['status_cd'].tobytes(), dtype='<U1').reshape(len(arr_customer), -1)
arr_customer[np.in1d(statud_cd_split[:, 0], np.fromiter('ABCDEF', dtype='<U1'))
& np.in1d(statud_cd_split[:, -1], np.fromiter('123456789', dtype='<U1'))][:10]
# 3.94 ms ± 17.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
P_016
P-016: 店舗データフレーム(df_store)から、電話番号(tel_no)が3桁-3桁-4桁のデータを全項目表示せよ。
ここまでくると正規表現モジュールre
を使うべきでしょうか。
import re
arr_store[[bool(re.fullmatch(r'[0-9]{3}-[0-9]{3}-[0-9]{4}', item))
for item in arr_store['tel_no'].tolist()]]
今回の場合、データを見ると10桁の電話番号しかなく、またバグった値もなかったので、「4文字目と8文字目が“-”の行」という問題に置換することができました。データに合わせて柔軟に問題を読み替えるのも解法の1つということで……。
tel_no_split = np.frombuffer(arr_store['tel_no'].tobytes(),
dtype='<U1').reshape(len(arr_store), -1)
arr_store[(tel_no_split[:, 3] == '-') & (tel_no_split[:, 7] == '-')]
array([('S12014', '千草台店', 12, '千葉県', '千葉県千葉市稲毛区千草台一丁目', 'チバケンチバシイナゲクチグサダイイッチョウメ', '043-123-4003', 140.118 , 35.63559, 1698.),
('S13002', '国分寺店', 13, '東京都', '東京都国分寺市本多二丁目', 'トウキョウトコクブンジシホンダニチョウメ', '042-123-4008', 139.4802, 35.70566, 1735.),
('S14010', '菊名店', 14, '神奈川県', '神奈川県横浜市港北区菊名一丁目', 'カナガワケンヨコハマシコウホククキクナイッチョウメ', '045-123-4032', 139.6326, 35.50049, 1732.),
('S14033', '阿久和店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4043', 139.4961, 35.45918, 1495.),
('S14036', '相模原中央店', 14, '神奈川県', '神奈川県相模原市中央二丁目', 'カナガワケンサガミハラシチュウオウニチョウメ', '042-123-4045', 139.3716, 35.57327, 1679.),
('S14040', '長津田店', 14, '神奈川県', '神奈川県横浜市緑区長津田みなみ台五丁目', 'カナガワケンヨコハマシミドリクナガツタミナミダイゴチョウメ', '045-123-4046', 139.4994, 35.52398, 1548.),
('S14050', '阿久和西店', 14, '神奈川県', '神奈川県横浜市瀬谷区阿久和西一丁目', 'カナガワケンヨコハマシセヤクアクワニシイッチョウメ', '045-123-4053', 139.4961, 35.45918, 1830.),
('S13052', '森野店', 13, '東京都', '東京都町田市森野三丁目', 'トウキョウトマチダシモリノサンチョウメ', '042-123-4030', 139.4383, 35.55293, 1087.),
('S14028', '二ツ橋店', 14, '神奈川県', '神奈川県横浜市瀬谷区二ツ橋町', 'カナガワケンヨコハマシセヤクフタツバシチョウ', '045-123-4042', 139.4963, 35.46304, 1574.),
('S14012', '本牧和田店', 14, '神奈川県', '神奈川県横浜市中区本牧和田', 'カナガワケンヨコハマシナカクホンモクワダ', '045-123-4034', 139.6582, 35.42156, 1341.),
('S14046', '北山田店', 14, '神奈川県', '神奈川県横浜市都筑区北山田一丁目', 'カナガワケンヨコハマシツヅキクキタヤマタイッチョウメ', '045-123-4049', 139.5916, 35.56189, 831.),
('S14022', '逗子店', 14, '神奈川県', '神奈川県逗子市逗子一丁目', 'カナガワケンズシシズシイッチョウメ', '046-123-4036', 139.5789, 35.29642, 1838.),
('S14011', '日吉本町店', 14, '神奈川県', '神奈川県横浜市港北区日吉本町四丁目', 'カナガワケンヨコハマシコウホククヒヨシホンチョウヨンチョウメ', '045-123-4033', 139.6316, 35.54655, 890.),
('S13016', '小金井店', 13, '東京都', '東京都小金井市本町一丁目', 'トウキョウトコガネイシホンチョウイッチョウメ', '042-123-4015', 139.5094, 35.70018, 1399.),
('S14034', '川崎野川店', 14, '神奈川県', '神奈川県川崎市宮前区野川', 'カナガワケンカワサキシミヤマエクノガワ', '044-123-4044', 139.5998, 35.57693, 1318.),
('S14048', '中川中央店', 14, '神奈川県', '神奈川県横浜市都筑区中川中央二丁目', 'カナガワケンヨコハマシツヅキクナカガワチュウオウニチョウメ', '045-123-4051', 139.5758, 35.54912, 1657.),
('S12007', '佐倉店', 12, '千葉県', '千葉県佐倉市上志津', 'チバケンサクラシカミシヅ', '043-123-4001', 140.1452, 35.71872, 1895.),
('S14026', '辻堂西海岸店', 14, '神奈川県', '神奈川県藤沢市辻堂西海岸二丁目', 'カナガワケンフジサワシツジドウニシカイガンニチョウメ', '046-123-4040', 139.4466, 35.32464, 1732.),
('S13041', '八王子店', 13, '東京都', '東京都八王子市大塚', 'トウキョウトハチオウジシオオツカ', '042-123-4026', 139.4235, 35.63787, 810.),
('S14049', '川崎大師店', 14, '神奈川県', '神奈川県川崎市川崎区中瀬三丁目', 'カナガワケンカワサキシカワサキクナカゼサンチョウメ', '044-123-4052', 139.7327, 35.53759, 962.),
('S14023', '川崎店', 14, '神奈川県', '神奈川県川崎市川崎区本町二丁目', 'カナガワケンカワサキシカワサキクホンチョウニチョウメ', '044-123-4037', 139.7028, 35.53599, 1804.),
('S13018', '清瀬店', 13, '東京都', '東京都清瀬市松山一丁目', 'トウキョウトキヨセシマツヤマイッチョウメ', '042-123-4017', 139.5178, 35.76885, 1220.),
('S14027', '南藤沢店', 14, '神奈川県', '神奈川県藤沢市南藤沢', 'カナガワケンフジサワシミナミフジサワ', '046-123-4041', 139.4896, 35.33762, 1521.),
('S14021', '伊勢原店', 14, '神奈川県', '神奈川県伊勢原市伊勢原四丁目', 'カナガワケンイセハラシイセハラヨンチョウメ', '046-123-4035', 139.3129, 35.40169, 962.),
('S14047', '相模原店', 14, '神奈川県', '神奈川県相模原市千代田六丁目', 'カナガワケンサガミハラシチヨダロクチョウメ', '042-123-4050', 139.3748, 35.55959, 1047.),
('S12013', '習志野店', 12, '千葉県', '千葉県習志野市芝園一丁目', 'チバケンナラシノシシバゾノイッチョウメ', '047-123-4002', 140.022 , 35.66122, 808.),
('S14042', '新山下店', 14, '神奈川県', '神奈川県横浜市中区新山下二丁目', 'カナガワケンヨコハマシナカクシンヤマシタニチョウメ', '045-123-4047', 139.6593, 35.43894, 1044.),
('S12030', '八幡店', 12, '千葉県', '千葉県市川市八幡三丁目', 'チバケンイチカワシヤワタサンチョウメ', '047-123-4005', 139.924 , 35.72318, 1162.),
('S14025', '大和店', 14, '神奈川県', '神奈川県大和市下和田', 'カナガワケンヤマトシシモワダ', '046-123-4039', 139.468 , 35.43414, 1011.),
('S14045', '厚木店', 14, '神奈川県', '神奈川県厚木市中町二丁目', 'カナガワケンアツギシナカチョウニチョウメ', '046-123-4048', 139.3651, 35.44182, 980.),
('S12029', '東野店', 12, '千葉県', '千葉県浦安市東野一丁目', 'チバケンウラヤスシヒガシノイッチョウメ', '047-123-4004', 139.8968, 35.65086, 1101.),
('S12053', '高洲店', 12, '千葉県', '千葉県浦安市高洲五丁目', 'チバケンウラヤスシタカスゴチョウメ', '047-123-4006', 139.9176, 35.63755, 1555.),
('S14024', '三田店', 14, '神奈川県', '神奈川県川崎市多摩区三田四丁目', 'カナガワケンカワサキシタマクミタヨンチョウメ', '044-123-4038', 139.5424, 35.6077 , 972.),
('S14006', '葛が谷店', 14, '神奈川県', '神奈川県横浜市都筑区葛が谷', 'カナガワケンヨコハマシツヅキククズガヤ', '045-123-4031', 139.5633, 35.53573, 1886.)],
dtype=[('store_cd', '<U6'), ('store_name', '<U6'), ('prefecture_cd', '<i4'), ('prefecture', '<U4'), ('address', '<U19'), ('address_kana', '<U30'), ('tel_no', '<U12'), ('longitude', '<f8'), ('latitude', '<f8'), ('floor_area', '<f8')])
なんでこの問題は全部出力しないといけないんでしょうか。