Pandas
前回、Pythonでプログラムを終了させる方法(sys.exit)を紹介しました。
今回はPandasで日時のデータが入った列を丸ごとdatetime型に変換する方法とさらに秒に変換する方法を紹介します。
ということでまずはStr型で日時のデータが入ったデータフレームを作成します。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year}/{month}/{day} {hour}:{minute}:{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
print(df)
print(type(df["Time"][3]))
まず最初に出力するデータ数を「data_num」で定義しています。
そしてrange関数を使ってそれぞれ年、月、日、時、分、秒の範囲を設定しています。
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
次にrandom.choiceを用いてランダムに年、月、日を取得していきます。
ただし2月は28日まで(閏年は考慮していません)、4月、6月、9月、11月は30日まで、そして他の月は31日までとするため、if文を使って月によって条件分岐しています。
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
月、日、時、分、秒は1桁の場合はゼロ埋めして2桁にしています。
(月は上の場所で、取得した際にゼロ埋めしています)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
これでそれぞれの値が取得できたので、Str型としてまとめて、リストに格納します。
selected_datetime = f"{year}/{month}/{day} {hour}:{minute}:{second}"
time_list.append(selected_datetime)
最後にPandasのデータフレームに入れて完了です。
df = pd.DataFrame(time_list, columns=["Time"])
Print関数で表示してみるとこんな感じになります。
print(df)
print(type(df["Time"][3]))
実行結果
Time
0 2017/08/25 14:34:27
1 2011/07/22 20:33:18
2 2006/01/01 09:09:09
3 2003/09/23 10:27:34
4 2020/07/09 06:34:45
5 2003/08/25 16:48:59
6 2012/02/21 09:34:53
7 2006/03/29 11:30:45
8 2023/10/26 15:20:42
9 2017/06/10 07:17:46
<class 'str'>
これで準備完了です。
pd.to_datetime(列)
データフレームの列を丸ごとdatetime型にするには「pd.to_datetime(列)」を使用します。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year}/{month}/{day} {hour}:{minute}:{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
df["Time"] = pd.to_datetime(df["Time"])
print(df)
print(type(df["Time"][3]))
実行結果
Time
0 2020-10-09 02:50:11
1 2017-11-29 03:30:08
2 2003-11-20 16:59:36
3 2010-08-15 05:28:32
4 2000-07-02 14:51:02
5 2003-10-05 18:08:03
6 2014-07-04 17:48:43
7 2004-10-11 06:38:39
8 2019-01-25 03:55:30
9 2003-02-18 23:13:58
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
私がよくやるミスは「列.to_datetime()」としてしまうことですが、もちろんこれでは動きませんので、ご注意あれ。
pd.to_datetime(列, format=”%Y%m…”)
ちなみに先ほどの例では日時の文字列が典型的なフォーマットであり、年、月、日、時、分、秒が読み取れる形式だったため、フォーマットを指定しなくても変換することができました。
しかし特殊なフォーマットの場合は、そのフォーマットを指定してあげる必要があります。
例えば「2001,12,05 14;08;04」のようなフォーマットの場合、フォーマットを指定しないとエラーとなります。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year},{month},{day} {hour};{minute};{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
df["Time"] = pd.to_datetime(df["Time"])
print(df)
print(type(df["Time"][3]))
実行結果
---------------------------------------------------------------------------
ParserError Traceback (most recent call last)
File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/_libs/tslib.pyx:605, in pandas._libs.tslib.array_to_datetime()
(中略)
File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/dateutil/parser/_parser.py:643, in parser.parse(self, timestr, default, ignoretz, tzinfos, **kwargs)
640 res, skipped_tokens = self._parse(timestr, **kwargs)
642 if res is None:
--> 643 raise ParserError("Unknown string format: %s", timestr)
645 if len(res) == 0:
646 raise ParserError("String does not contain a date: %s", timestr)
ParserError: Unknown string format: 2009,08,05 12;02;25 present at position 0
フォーマットを指定するには「pd.to_datetime(列, format=”%Y%m…”)」とします。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year},{month},{day} {hour};{minute};{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
df["Time"] = pd.to_datetime(df["Time"], format="%Y,%m,%d %H;%M;%S")
print(df)
print(type(df["Time"][3]))
実行結果
Time
0 2005-09-12 14:04:28
1 2016-10-18 11:42:09
2 2014-11-25 10:26:45
3 2013-10-26 07:31:59
4 2014-12-16 02:20:55
5 2003-06-12 14:15:58
6 2014-05-26 00:55:19
7 2008-09-17 13:21:47
8 2003-11-24 10:22:46
9 2004-09-21 00:36:41
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
フォーマットの書き方に関してはこちらの記事を参考にしてもらえたら幸いです。
秒数に変換する方法:dt.total_seconds()
次にPandasで列を秒数に変換する方法です。
この場合は「列.dt.total_seconds()」を使いますが、datetime型では変換できません。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year},{month},{day} {hour};{minute};{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
df["Time"] = pd.to_datetime(df["Time"], format="%Y,%m,%d %H;%M;%S")
df["Second"] = df["Time"].dt.total_seconds()
print(df)
実行結果
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[3], line 34
30 df = pd.DataFrame(time_list, columns=["Time"])
32 df["Time"] = pd.to_datetime(df["Time"], format="%Y,%m,%d %H;%M;%S")
---> 34 df["Second"] = df["Time"].dt.total_seconds()
36 print(df)
AttributeError: 'DatetimeProperties' object has no attribute 'total_seconds'
「.dt.total_seconds()」を使って秒に変換するにはデータをtimedelta型に変換する必要があります。
datatime型からtimedelta型に変換するには、2つのdatetime型のデータを引き算します。
そして得られたデータの列に対して「.dt.total_seconds()」を使うと経過秒数が得られます。
例えば最初の行のデータで他の行のデータを引き算するとこんな感じになります。
import pandas as pd
import random
data_num = 10
year_list = range(2000, 2024)
month_list = range(1, 13)
hour_list = range(0, 24)
minute_list = range(0, 60)
second_list = range(0, 60)
time_list = []
for i in range(data_num):
year = str(random.choice(year_list))
month = str(random.choice(month_list)).zfill(2)
if month == "02":
day_list = range(1, 29)
elif month == "04" or month == "06" or month == "09" or month == "11":
day_list = range(1, 31)
else:
day_list = range(1, 32)
day = str(random.choice(day_list)).zfill(2)
hour = str(random.choice(hour_list)).zfill(2)
minute = str(random.choice(minute_list)).zfill(2)
second = str(random.choice(second_list)).zfill(2)
selected_datetime = f"{year},{month},{day} {hour};{minute};{second}"
time_list.append(selected_datetime)
df = pd.DataFrame(time_list, columns=["Time"])
df["Time"] = pd.to_datetime(df["Time"], format="%Y,%m,%d %H;%M;%S")
df["TimeDiff"] = df["Time"] - df["Time"][0]
df["Second"] = df["TimeDiff"].dt.total_seconds()
print(df)
実行結果
Time TimeDiff Second
0 2012-02-28 17:40:52 0 days 00:00:00 0.0
1 2015-07-10 09:40:01 1227 days 15:59:09 106070349.0
2 2018-11-06 10:16:22 2442 days 16:35:30 211048530.0
3 2011-09-25 06:08:18 -157 days +12:27:26 -13519954.0
4 2020-05-09 03:27:55 2992 days 09:47:03 258544023.0
5 2020-06-19 03:10:22 3033 days 09:29:30 262085370.0
6 2023-05-06 07:41:51 4084 days 14:00:59 352908059.0
7 2005-12-09 08:50:40 -2273 days +15:09:48 -196332612.0
8 2002-12-05 16:10:46 -3373 days +22:29:54 -291346206.0
9 2007-06-20 10:35:04 -1715 days +16:54:12 -148115148.0
こちらも意外とtimedelta型にせずに「.dt.total_seconds()」を使いがちだったりします。
次回はあまり気にせずに使っていたmatplotlibの「tight_layout()」の挙動をみてみようと思います。
ではでは今回はこんな感じで。
コメント