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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1275閲覧

文字列(object、float64)をdatetime型に変更したい

退会済みユーザー

退会済みユーザー

総合スコア0

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/12/02 06:51

編集2021/12/02 07:18

PythonのPandasでデータの前処理を行っていますが、初心者です。
時間の集積を行いたいので、時刻の部分をdatetime型に変更したいのですが、
分が‘で入っていることと、分がなく秒から始まっている箇所があるために、
セオリー通り?の
pd.to_datetime(df['a'], format='%H時%M分S秒')
ではうまくいかないため、どうしたらよいものか考えあぐねております・・

df
a    b   c
2'40.24 46.32 1'56.66
2'00.76 20.64 49.33

df.dtypes

a object
b float64
c object

これを、
df
a        b      c
00:02:40.24 00:00:46.32 00:01:56.66
00:02:00.76 00:00:20.64 00:00:49.33

df.dtypes

a datetime
b datetime
c datetime
このようにしたいです。

こういった場合、どのように処理したらよいでしょうか。。
知恵をお借りしたくよろしくお願いいたします。

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

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

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

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

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

bsdfan

2021/12/02 07:53

dfはどうやって作成したものでしょうか? csvから読み取ったとかですか? 分がなくてfloatになってしまっているものは、dfに取り込む時点で文字列として処理するようにしたほうが、全体としてすっきりしそうです。
退会済みユーザー

退会済みユーザー

2021/12/02 08:12 編集

そうです。もともとはCSVになっています。 なるほど、、そこでまずひと手間入れたほうがよいのですね。。 試してみます。
guest

回答3

0

read_csvでdtypeをobjectに指定して、文字列として取り込んでおいて、
最初に、分ありのパタンでdatetimeに変換して、
そこで変換できなかった部分(NaT)を、分なしのパタンで変換して、
fillnaで両者を結合します。

python

1import pandas as pd 2 3df = pd.read_csv('foo.csv', dtype={'a': object, 'b': object, 'c': object}) 4 5for col in ['a', 'b', 'c']: 6 pat1 = pd.to_datetime(df[col], format="%M'%S.%f", errors='coerce') 7 pat2 = pd.to_datetime(df.loc[pat1.isna(), col], format="%S.%f", errors='coerce') 8 df[col] = pat1.fillna(pat2) 9 10print(df)

回答とはあまり関係ないのですが、datetimeにするよりもtimedeltaにするほうがいいのではないかという気がします。

追記

timedeltaにする場合は、forの最後を下記のようにして、'1900-01-01 00:00:00'からの差分にするのが簡単だと思います。

python

1 df[col] = pat1.fillna(pat2) - pd.to_datetime('0.0', format='%S.%f')

投稿2021/12/02 08:38

編集2021/12/03 01:44
bsdfan

総合スコア4794

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

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

退会済みユーザー

退会済みユーザー

2021/12/03 01:33

bsdfan様 追ってのご対応ありがとうございます。実は頂いた内容でとても助かりまして、、 最初の方法だと、df.dtypesで確認すると、object型のままで変化がなく、 最終的にはこの時刻の値で集積和を取っていきたかったので、timedeltaで変換しなければできないことに この後気が付きました。 ただ、今度は、今回不要な年月日までがカラムに表示されてしまい、そのためか 正確な集積和が取れない状況になっております・・ a      00:02:40.24  00:02:00.76  だったものが a 1900-01-01 00:02:40.244 1900-01-01 00:02:08.958 1900-01-01 00:05:51.515 1900-01-01 00:01:50.391 1900-01-01 00:01:48.452 これをそのまま.cumsum()してしまうと sum 1900-01-01 00:02:40.244000000 1830-01-01 00:04:49.202000000 1760-01-01 00:10:40.717000000 1689-12-31 00:12:31.108000000 2204-07-21 23:48:53.269551616 ¦ という状況でして現在見直しております。
退会済みユーザー

退会済みユーザー

2021/12/03 02:17

df[col] = pat1.fillna(pat2) - pd.to_datetime('0.0', format='%S.%f') 差分を取ればよいので確かにわかりやすいですね・・! こうすると「0 days 00:02:40.244000」となるのですが、0 days がつくのはtimedelta64[ns]だと常なのですね。 時刻変換に結構はまるものだと今回痛感しました・・
bsdfan

2021/12/03 02:28

私もtimedeltaへの不満は多いです・・・ 結局、total_seconds でfloatにして扱ってしまうのが楽だったりもしますね。
退会済みユーザー

退会済みユーザー

2021/12/03 02:33

さしあたり df["a"] = df["a"].map(lambda x: x.total_seconds()) これで秒換算できそうなので、こうしてみることにします。 色々をご相談できて助かりました。ありがとうございます。
退会済みユーザー

退会済みユーザー

2021/12/03 02:44

最終的に取りまとめますと、 df a    b   c 2'40.244 NaN 46.31 2'08.958 22.684 39.243 5'51.515 4'18.613 38.791 ¦ df.dtypes a object b float64 c object これを df = pd.read_csv("*.csv", dtype={"a": object, "b": object, "c": object,}) for col in ["a", "b", "c"]: pat1 = pd.to_datetime(df[col], format="%M'%S.%f", errors='coerce') pat2 = pd.to_datetime(df.loc[pat1.isna(), col], format="%S.%f", errors='coerce') df[col] = pat1.fillna(pat2) df[col] = pat1.fillna(pat2) - pd.to_datetime('0.0', format='%S.%f') #秒換算 df["a"] = df["a"].map(lambda x: x.total_seconds()) #タイム集積 df["d"] = df["a"].cumsum() とすることで、 a d b c 160.244 160.244 NaN 46.310 128.958 289.202 22.684 39.243 351.515 640.717 258.613 38.791 と、やりたいことにすることができました。
guest

0

float 部分では、intに変換することで(小数点切り捨て)秒が、
1000倍して1000の余りを取ることでmsecが取れますんで、それでdatetimeに変換しては

投稿2021/12/02 07:51

y_waiwai

総合スコア88042

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

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

退会済みユーザー

退会済みユーザー

2021/12/02 08:31

ありがとうございます・・いろいろな方法があるのですね。 元データを見ると、秒ばかりで終わっているファイルはfloat64になっていますが、 分が入っているカラムが一つでもあるとobjectになっていて、ファイルごとに混在している状況で、 objectの列になってしまっている列はintに変えられなくて、思うようにいかずでして・・
y_waiwai

2021/12/02 08:36

ああ、全て文字列なら、’文字で分離させて分、.で分離させて秒、そのあとは後ろに0つけてmsec、と分けれますんで、それで再構成させるとか。 まあ、別回答のパターンでエラーが出るってのはそれに当てはまらないデータが有るってことなんで、それを示してもらわないと解決できませんねー
guest

0

ベストアンサー

2'40.24 というのは 2分40秒240ミリセカンドの事かと思います。データには NaN が含まれているとの事ですので、以下のコードでは NaN00:00:00 に変換する様にしています。

python

1df["column_c"] = df["column_c"].apply(lambda x: 2 pd.to_datetime("0.0" if str(x) == "nan" else str(x), 3 format=("%M'%S.%f" if "'" in str(x) else "%S.%f"), 4 errors='coerce').time())

投稿2021/12/02 07:18

編集2021/12/02 09:38
melian

総合スコア20655

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

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

退会済みユーザー

退会済みユーザー

2021/12/02 07:33

半日悩んだことが瞬殺で感動しました・・ ただ一点、cの「49.33」が本来なら「0‘49.33」のことなものの、そうなっていないがために NaTになってしまう点は、元のデータをいじる必要がある?ということでしょうか・・
melian

2021/12/02 07:39

一応、元のデータをいじらなくても解決できそうではありそうです。分かり次第、回答を修正します。
melian

2021/12/02 07:47

少し捕捉すると、float でもなく、"%M'%S.%f" 形式の文字列でもないデータが含まれている場合はエラーになります。
退会済みユーザー

退会済みユーザー

2021/12/02 07:52

一緒に考えて頂いてありがとうございます・・ 元データを見ると、秒ばかりで終わっているファイルはfloat64になっていますが、 分が入っているカラムが一つでもあるとobjectになっていて、ファイルごとに混在している状況です。
退会済みユーザー

退会済みユーザー

2021/12/02 08:10

時間以外のカラムもあるため、 dfx = df.applymap(lambda x: pd.to_datetime(str(x), format=("%S.%f" if isinstance(x, float) else "%M'%S.%f"), errors='coerce').time()) で実行すると、 AttributeError: 'Series' object has no attribute 'applymap' となってしまいました。。列指定できないのですね、、
melian

2021/12/02 08:14

その場合は、 df['a'] = df['a'].apply(...) とします。
退会済みユーザー

退会済みユーザー

2021/12/02 08:28

そうすると、たぶん 「float でもなく、"%M'%S.%f" 形式の文字列でもないデータが含まれている場合はエラーになります。」 ここが効いて? ValueError: NaTType does not support time になってしまいます・・ 上の方でbsdfan様が「dfに取り込む時点で文字列として処理する」ことを仰られていたのを試し、 全部の列をobjectに変えたら、うまくいった列も上記の ValueError: NaTType does not support time が出るようになったので、そういうことなのですかね・・汗 難しい・・
melian

2021/12/02 08:30

具体的にどの様な値でエラーになっているのかが判れば対処できるかもしれません。
退会済みユーザー

退会済みユーザー

2021/12/02 09:06 編集

実際に column_c NaN 22.684 4'18.613 20.444 20.29 となっている時間の列があり、ここでご教授頂きました df["column_c"] = df["column_c"].apply(lambda x: pd.to_datetime(str(x), format=("%S.%f" if isinstance(x, float) else "%M'%S.%f"), errors='coerce').time()) と実行すると、 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-69-baabb1a66f0b> in <module> 27 # errors='coerce').time()) 28 ---> 29 df["1"] = df["1"].apply(lambda x: 30 pd.to_datetime(str(x), 31 format=("%S.%f" if isinstance(x, float) else "%M'%S.%f"), ~/.pyenv/versions/3.9.0/lib/python3.9/site-packages/pandas/core/series.py in apply(self, func, convert_dtype, args, **kwds) 4133 else: 4134 values = self.astype(object)._values -> 4135 mapped = lib.map_infer(values, f, convert=convert_dtype) 4136 4137 if len(mapped) and isinstance(mapped[0], Series): pandas/_libs/lib.pyx in pandas._libs.lib.map_infer() <ipython-input-69-baabb1a66f0b> in <lambda>(x) 28 29 df["1"] = df["1"].apply(lambda x: ---> 30 pd.to_datetime(str(x), 31 format=("%S.%f" if isinstance(x, float) else "%M'%S.%f"), 32 errors='coerce').time()) pandas/_libs/tslibs/nattype.pyx in pandas._libs.tslibs.nattype._make_error_func.f() ValueError: NaTType does not support time というエラーとなっています。 column_cの型はobjectです。 NaNがない別の同様の列でもエラーになるので、NaNは関係ないように思います。 これを別の列column_d(ここにはすべて分が入っている)で実行するとエラーが出ず意図する形となります。 column_d 2'40.244 2'08.958 5'51.515 1'50.391 1'48.452 ¦ ↓ (実行後) 00:02:40.244000 00:02:08.958000 00:05:51.515000 00:01:50.391000 00:01:48.452000 型はcolumn_dもobjectです。
melian

2021/12/02 08:52

NaN は float として扱われますのでエラーになってしまいます。また、例えば 65.24 などという数値もエラーになります(60秒以上の表記はエラー)。
melian

2021/12/02 09:04

df["column_c"] = df["column_c"].apply(lambda x: pd.to_datetime("0.0" if str(x) == "nan" else str(x), format=("%M'%S.%f" if "'" in str(x) else "%S.%f"), errors='coerce').time()) を試してみていただけますか?
退会済みユーザー

退会済みユーザー

2021/12/02 09:08

(T_T)うまくいきました!!!!! 後学のため、中身をちょっと咀嚼させて頂きます!!
melian

2021/12/02 09:13

良かったです。ただ、NaN が 00:00:00 に変換されてしまいます。NaN のままにしたい場合はコメントして下さい。
退会済みユーザー

退会済みユーザー

2021/12/02 09:25

00:00:00を後からNaNに変換することも、安直なのですが本件なら大丈夫な気がします・・ (00:00:00という計測は起こらないと思われるので) 最後までお付き合いいただきまして、本当にありがとうございました。感謝いたします。
melian

2021/12/02 09:28

はい、了解です。後程、回答を編集します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問