質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

2回答

1433閲覧

年月表記のスマートな処理の仕方

gogotowel

総合スコア9

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

1クリップ

投稿2022/01/08 00:20

お世話になります。
教えてください。
フォーマットの異なる年月表記を統一したいと考えています。
下表のイメージです。

処理前処理後
2018-04-102018年4月
2018-05-112018年5月
平成10年09月1998年9月
昭和47年12月1972年12月

そこで自分で書いたコードがこちらなのですが、我ながらなんとも冗長なコードになってしまい、
もっとスマートな書き方があればぜひご教示いただきたいです。よろしくお願いいたします。

python

1import pandas as pd 2 3df = pd.DataFrame({ 4 '基準日':["2018-04-10", 5 "2018-05-11", 6 "平成30年09月", 7 "昭和47年12月",] 8}) 9 10df["年号"] = df["基準日"].str[:2] 11df["基準日"] = df["基準日"].str.replace("昭和", '@') 12df["基準日"] = df["基準日"].str.replace("平成", '@') 13 14df["和暦抽出用"] = df["基準日"].str.split("@", expand=True)[1] 15df["和暦年"] = df["和暦抽出用"].str.split("年", expand=True)[0] 16df["和暦年"].fillna(0, inplace=True) 17df["和暦月"] = df["和暦抽出用"].str.split("年", expand=True)[1] 18df["和暦月2"] = df["和暦月"].str.split("月", expand=True)[0] 19 20df["西暦"] = df["和暦年"].astype(int) 21df.loc[df["年号"]=="平成","西暦"] = df["西暦"] + 1988 22df.loc[df["年号"]=="昭和","西暦"] = df["西暦"] + 1925 23df.loc[df["年号"]=="令和","西暦"] = df["西暦"] + 2019 24df["西暦年月"] = df["西暦"].map(str) + "-" + df["和暦月2"] + "-01" 25 26df["西暦年月"] = pd.to_datetime(df["西暦年月"]) 27df["西暦年"] = df["西暦年月"].dt.year 28df['西暦年'] = df['西暦年'].dropna().apply(int).apply(str) 29 30df["西暦月"] = df["西暦年月"].dt.month 31df['西暦月'] = df['西暦月'].dropna().apply(int).apply(str) 32 33df.loc[df["基準日"].str.contains("-"),"西暦年"] = df["基準日"].str[:4] 34df.loc[df["基準日"].str.contains("-"),"西暦月"] = df["基準日"].str[5:7] 35df["西暦年月"] = df["西暦年"] +"年"+ df["西暦月"]+"月"

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gogotowel

2022/01/11 11:46

ありがとうございました。
guest

回答2

0

python

1import pandas as pd 2 3pd.set_option('display.unicode.east_asian_width', True) 4 5df = pd.DataFrame({ 6 '基準日':["2018-04-10", 7 "2018-05-11", 8 "平成30年09月", 9 "昭和47年12月",] 10}) 11 12start = {'明治': 1867, '大正': 1911, '昭和': 1925, '平成': 1988, '令和': 2018} 13df['西暦年月'] = ( 14 df['基準日'].str 15 .extract(r'((\d{4})-(\d{2})|([^\d]+)(\d+)年(\d{2})月)') 16 .fillna(0).astype({1: int, 2: int, 4: int, 5: int}) 17 .apply( 18 lambda x: 19 f'{x[1]}{x[2]}月' if x[1] else f'{start[x[3]]+x[4]}{x[5]}月', axis=1) 20) 21 22print(df) 23 24# 25 基準日 西暦年月 260 2018-04-10 20184271 2018-05-11 20185282 平成300920189293 昭和4712197212

投稿2022/01/08 03:39

melian

総合スコア19712

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gogotowel

2022/01/11 11:44

ご回答ありがとうございました。 分かりやすさでcan110様をベストアンサーとさせていただきましたが、melian様のご回答も試してみたいと思います。大変ありがとうございました。
guest

0

ベストアンサー

  • 年月の抽出に正規表現を使う
  • applyで一括処理する

以上で以下のように書けます。

Python

1import pandas as pd 2import re 3 4df = pd.DataFrame({ 5 '基準日':["2018-04-10", 6 "2018-05-11", 7 "平成30年09月", 8 "昭和47年12月",] 9}) 10 11def conv(s): 12 # 年月を抽出 13 rules = [(r'(\d+)-(\d+)', 0), (r'平成(\d+)年(\d+)月', 1988), (r'昭和(\d+)年(\d+)月', 1925)] 14 y, m = None, None 15 for exp, offset in rules: 16 ret = re.search(exp, s) 17 if ret: 18 y = int(ret.group(1)) + offset 19 m = int(ret.group(2)) 20 break 21 22 s = '' 23 if y and m: 24 s = f'{y}{m}月' 25 return s 26 27df['西暦年月'] = df['基準日'].apply(conv) 28print(df) 29# 基準日 西暦年月 30#0 2018-04-10 2018年4月 31#1 2018-05-11 2018年5月 32#2 平成30年09月 2018年9月 33#3 昭和47年12月 1972年12月 34 35

投稿2022/01/08 01:00

編集2022/01/08 01:10
can110

総合スコア38262

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gogotowel

2022/01/10 21:42

ご回答ありがとうございました。 いただいた回答をいろいろ試して返答が遅くなりました。 applyの処理について教えてください。 conv()がapplyで実行される際、引数sはどのように与えられるのでしょうか。 というのも、手持ちのデータで試すと次のエラーが出るのです。 ------------ ~\AppData\Local\Temp/ipykernel_9412/200884706.py in conv(s) 17 y,m = None,None 18 for exp,offset in rules: ---> 19 ret = re.search(exp,s) 20 if ret : 21 y = int(ret.group(1)) + offset ~\AppData\Local\Programs\Python\Python310\lib\re.py in search(pattern, string, flags) 198 """Scan through string looking for a match to the pattern, returning 199 a Match object, or None if no match was found.""" --> 200 return _compile(pattern, flags).search(string) 201 202 def sub(pattern, repl, string, count=0, flags=0): TypeError: expected string or bytes-like object ------------ 原因はメッセージの通り与えられた引数sが文字列じゃないということなのでしょうが、要素はobjectであることを確認しております。ご提示いただいたコードではエラーなく動作するので、データ側の問題だとは思うのですが、ヒントをいただけるとありがたいです。よろしくお願いいたします。
can110

2022/01/11 00:55

applyの詳細については以下マニュアルを参照ください。 https://pandas.pydata.org/docs/reference/api/pandas.Series.apply.html 呼び出される関数の第一引数にシリーズの各値が入ってきます。 コード全体が不明なのでなんともいえませんが、おそらく意図しない引数を渡しているものと思います。
gogotowel

2022/01/11 11:41

元データ(excel)からDataFrameの変換時に文字列で読み込んだら解消しました。 df = pd.read_excel(path_to_file, dtype=str) 根本的な解決ではないのですが、この書き方で進めることとしました。 今回いただいたご回答で正規表現のいろはのいを学ぶことができて、大変参考になりました。 改めて、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問