向此 Dataframe 添加部分行时,为什么我得到的是 NaT 值而不是 NaN?

Why do I get NaT values rather than NaN when adding partial rows to this Dataframe?

我有一个脚本可以将 .csv 文件读入数据框,然后允许用户通过向其中添加额外数据来扩展数据框。它将采用 date 列中的最后一个值,并开始每天提示用户输入一个值。

如果用户没有为 input 指定任何内容,那么该值将被转换为 math.nan。除了当我将该行附加到数据框时,假设的 NaN 被转换为 NaT.

我在下面重新创建了一个可重现的示例。

如何确保我的 NaN 不会转换为 NaT

#!/usr/bin/env python

import pandas as pd
import datetime as dt
import math

df = pd.DataFrame({
    'date': pd.to_datetime(['2022-05-01', '2022-05-02', '2022-05-03']),
    'weight': [250., 249, 247],
})

last_recorded_date = df['date'].iloc[-1]
next_date = last_recorded_date + dt.timedelta(days=1)
df.loc[len(df.index)] = [next_date, math.nan]

print(df)
#         date weight
# 0 2022-05-01  250.0
# 1 2022-05-02  249.0
# 2 2022-05-03  247.0
# 3 2022-05-06    NaT
import pandas as pd
import datetime as dt
import math

df = pd.DataFrame({
    'date': pd.to_datetime(['2022-05-01', '2022-05-02', '2022-05-03']),
    'weight': [250., 249, 247],
    })

last_recorded_date = df['date'].iloc[-1]

while True:
    next_date = last_recorded_date + dt.timedelta(days=1)
    weight = input(f"{next_date}: ")
    if weight == 'q':
        break
    elif weight == '':
        weight = math.nan
    else:
        weight = float(weight)

    df.loc[len(df.index)] = [next_date, weight]
    last_recorded_date = next_date

df = df['weight'].replace(pd.NaT, math.nan)

print(df)

这很奇怪。但一些实验揭示了一些线索:

df = pd.DataFrame({
    'date': pd.to_datetime(['2022-05-01', '2022-05-02', '2022-05-03']),
    'weight': [250., 249, 247],
    })

# Try this
df.loc[4] = None

加薪:

FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.

这并不能完全解释为什么将 NaT 添加到第二列,但它确实表明在附加到现有数据帧时需要指定类型。

here所述,一种解决方案如下:

df = pd.DataFrame({
    'date': pd.to_datetime(['2022-05-01', '2022-05-02', '2022-05-03']),
    'weight': [250., 249, 247],
    })

next_date = pd.Timestamp('2022-05-04')
df = df.append(pd.DataFrame([{'date': next_date, 'weight': np.nan}]), ignore_index=True)
assert (df.dtypes.values == ('<M8[ns]', 'float64')).all()

然而,这引发了:

FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
  df = df.append(pd.DataFrame([{'date': next_date, 'weight': np.nan}]), ignore_index=True)

所以我想现在正确的解决方案是:

new_row = pd.DataFrame([{'date': next_date, 'weight': np.nan}])
df = pd.concat([df, new_row]).reset_index(drop=True)
assert (df.dtypes.values == ('<M8[ns]', 'float64')).all()

但我必须问,为什么要以这种方式附加到数据框?这是非常低效的,应该尽可能避免。

list 设置行时,列表首先转换为 SeriesSeries 的元素必须是同一类型;第一个值是 datetime;因此每个值都在结果 Series 中转换为 datetime。特别是,math.nan 变为 NaT。 Pandas使用现有的列类型来通知进程;相反,列类型会根据需要进行调整 - weight 列的类型从 float 扩展为 object.

根据我的测试,使用元组似乎可以解决问题:

df.loc[len(df.index)] = (next_date, math.nan)