首页 > 解决方案 > Pandas 将 nan 放在特定行('Feb-29')并将剩余的行向上移动

问题描述

我有一个熊猫数据框,其中包含几年的时间序列数据作为列。每个都从 11 月开始,到次年结束。我正在尝试在非闰年处理 NaN。可以使用以下内容重新创建结构:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

ndays = 151
sdates = [(datetime(2019,11,1) + timedelta(days=x)).strftime("%b-%d") for x in range(ndays)]
columns=list(range(2016,2021))
df = pd.DataFrame(np.random.randint(0,100,size=(ndays, len(columns))), index=sdates, columns=columns)
df.loc["Feb-29",2017:2019] = np.nan
df.loc["Feb-28":"Mar-01"]

Out[61]: 
        2016  2017  2018  2019  2020
Feb-28    36  59.0  74.0  19.0    24
Feb-29    85   NaN   NaN   NaN     6
Mar-01    24  75.0  49.0  99.0    82

我想要做的是仅删除“Feb-29”NaN 数据(在非闰年),然后将这些列中的数据向上移动一行,让闰年保持原样。像这样,Mar-01 和随后的行在 2017 年到 2019 年向上移动:

        2016  2017  2018  2019  2020
Feb-28    36  59.0  74.0  19.0    24
Feb-29    85  75.0  49.0  99.0     6
Mar-01    24  42.0  21.0  41.0    82

我不在乎“Mar-01”数据将被标记为“Feb-29”,因为最终我将用整数索引替换字符串日期索引。

请注意,我没有在示例中包含此内容,但在我不想删除的不同行中,我在数据帧的开头和结尾有 NaN(即,我不能只删除所有 NaN 数据,我需要定位“2月29日”具体)

标签: pythonpandasdataframenumpynan

解决方案


听起来您实际上并不想将日期向上移动,而是根据一年中的日期正确编号?如果是这样,这将起作用:

首先,使 DataFrame 长而不是宽:

df = pd.DataFrame(
    {
        "2016": {"Feb-28": 36, "Feb-29": 85, "Mar-01": 24},
        "2017": {"Feb-28": 59.0, "Feb-29": None, "Mar-01": 75.0},
        "2018": {"Feb-28": 74.0, "Feb-29": None, "Mar-01": 49.0},
        "2019": {"Feb-28": 19.0, "Feb-29": None, "Mar-01": 99.0},
        "2020": {"Feb-28": 24, "Feb-29": 6, "Mar-01": 82},
    }
)


df = (
    df.melt(ignore_index=False, var_name="year", value_name="value")
    .reset_index()
    .rename(columns={"index": "month-day"})
)
df
   month-day  year  value
0     Feb-28  2016   36.0
1     Feb-29  2016   85.0
2     Mar-01  2016   24.0
3     Feb-28  2017   59.0
4     Feb-29  2017    NaN
5     Mar-01  2017   75.0
6     Feb-28  2018   74.0
7     Feb-29  2018    NaN
8     Mar-01  2018   49.0
9     Feb-28  2019   19.0
10    Feb-29  2019    NaN
11    Mar-01  2019   99.0
12    Feb-28  2020   24.0
13    Feb-29  2020    6.0
14    Mar-01  2020   82.0

然后删除包含无效日期的行并获取剩余天数的年份:

df["date"] = pd.to_datetime(
    df.apply(lambda row: " ".join(row[["year", "month-day"]]), axis=1), errors="coerce",
)
df = df[df["date"].notna()]
df["day_of_year"] = df["date"].dt.dayofyear
df
   month-day  year  value       date  day_of_year
0     Feb-28  2016   36.0 2016-02-28           59
1     Feb-29  2016   85.0 2016-02-29           60
2     Mar-01  2016   24.0 2016-03-01           61
3     Feb-28  2017   59.0 2017-02-28           59
5     Mar-01  2017   75.0 2017-03-01           60
6     Feb-28  2018   74.0 2018-02-28           59
8     Mar-01  2018   49.0 2018-03-01           60
9     Feb-28  2019   19.0 2019-02-28           59
11    Mar-01  2019   99.0 2019-03-01           60
12    Feb-28  2020   24.0 2020-02-28           59
13    Feb-29  2020    6.0 2020-02-29           60
14    Mar-01  2020   82.0 2020-03-01           61

推荐阅读