Измените год на основе месяца в фрейме данных в python

У меня есть набор данных под названием погода, и он содержит один столбец «Дата», который выглядит следующим образом.

Дата 2020-01-01 2020-01-02 2020-02-01 2020-02-04 2020-03-01 2020-04-01 2020-04-02 2020-04-03 2020-04-04 2020-05-01 2020-06-01 2020-07-01 2020-08-01 2020-09-01 2020-10-01 2020-11-01 2020-01-01 2020-02-01 2020-04-01 2020-05-01 2020-06-01 2020-07-01 2020-08-01 2020-09-01 2020-10-01 2020-11-01 2020-12-01 2020-01-01

Проблема в том, что год всегда 2020, хотя должен быть 2020, 2021 и 2022.

Нужный столбец выглядит так

Дата 2020-01-01 2020-01-02 2020-02-01 2020-02-04 2020-03-01 2020-04-01 2020-04-02 2020-04-03 2020-04-04 2020-05-01 2020-06-01 2020-07-01 2020-08-01 2020-09-01 2020-10-01 2020-11-01 2021-01-01 2021-02-01 2021-04-01 2021-05-01 2021-06-01 2021-07-01 2021-08-01 2021-09-01 2021-10-01 2021-11-01 2021-12-01 2022-01-01

Последний месяц каждого года не обязательно равен 12, но новый год начинается с месяца 01.

Вот мой код:

month = ['01','02','03','04','05','06','07','08','09','10','11','12']
for i in range(len(weather['Date'])):
    year = 2022
    for j in range(len(month)):
        if weather['Date'][i][5:7] == '01':
            weather['Date'][i] = weather['Date'][i].apply(lambda x: 'year' + x[5:])

Есть ли какие-либо предложения по исправлению моего кода и получению желаемого столбца?

У вас есть одна строка данных за каждый месяц с 1 января 2020 года?

Amin S 12.02.2023 22:01

Вы имеете в виду year += 1?

Sheldon 12.02.2023 22:10

@AminS Спасибо, что указали на это! Нет, для каждого месяца есть несколько строк, и я изменил свой вопрос.

mmmmmm 12.02.2023 22:18

@Sheldon Да, это должен быть год += 1. Но для каждого месяца есть несколько строк, и, похоже, это не работает.

mmmmmm 12.02.2023 22:19
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
75
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот один подход:

  • Превратите строки даты в столбце Date в дату и время, используя pd.to_datetime и примените Series.diff и цепочку Series.dt.day.
  • Поскольку каждое отрицательное значение (то есть «день») в нашем Series будет представлять начало нового года, давайте применим Series.lt(0), чтобы превратить все значения ниже 0 в True, а остальные в False.
  • На этом этапе мы цепляем Series.cumsum, чтобы в итоге получилось Series, содержащее 0, ..., 1, ..., 2. Это будут значения, которые нужно добавить к году 2020, чтобы получить правильные годы.
  • Теперь, наконец, мы можем создать правильные даты, снова передав (new_year = year + addition), month, day в pd.to_datetime (см. этот ТАК ответ).
df['Date'] = pd.to_datetime(df['Date'])

df['Date'] = pd.to_datetime(dict(year=(df['Date'].dt.year 
                                       + df['Date'].diff().dt.days.lt(0).cumsum()), 
                                 month=df['Date'].dt.month, 
                                 day=df['Date'].dt.day))

df['Date']

0    2020-01-01
1    2020-01-02
2    2020-02-01
3    2020-02-04
4    2020-03-01
5    2020-04-01
6    2020-04-02
7    2020-04-03
8    2020-04-04
9    2020-05-01
10   2020-06-01
11   2020-07-01
12   2020-08-01
13   2020-09-01
14   2020-10-01
15   2020-11-01
16   2021-01-01
17   2021-02-01
18   2021-04-01
19   2021-05-01
20   2021-06-01
21   2021-07-01
22   2021-08-01
23   2021-09-01
24   2021-10-01
25   2021-11-01
26   2021-12-01
27   2022-01-01
Name: Date, dtype: datetime64[ns]

Конечно, вам не нужно конвертировать в datetime. Вы также можете воссоздать строки даты, оставив следующую строку:

df['Date'].str[5:7].astype(int).diff().lt(0).cumsum()

Аналогично @ouroboros1, но с использованием numpy, чтобы получить количество лет, которое нужно добавить к каждой дате, а затем pd.offsets.DateOffset(years=...) для добавления.

import numpy as np
import pandas as pd

df['Date'] = pd.to_datetime(df['Date'])
s = df['Date'].values
y = np.r_[0, (s[:-1] > s[1:]).cumsum()]

На этом этапе было бы заманчиво сделать:

df['Date'] += y * pd.offsets.DateOffset(years=1)

Но тогда мы получим предупреждение: PerformanceWarning: Adding/subtracting object-dtype array to DatetimeArray not vectorized.

Поэтому вместо этого мы группируем по количеству лет для добавления и добавляем соответствующее смещение ко всем датам в группе.

def add_years(g):
    return g['Date'] + pd.offsets.DateOffset(years=g['y'].iloc[0])

df['Date'] = df.assign(y=y).groupby('y', sort=False, group_keys=False).apply(add_years)

Это достаточно быстро (4,25 мс для 1000 строк и 10 различных значений y) и для других ситуаций, отличных от вашей, немного более общее, чем ответ @ouroboros1:

  1. Он обрабатывает изменения даты из-за високосного года (отсутствует в вашем примере, где все даты приходятся на первое число месяца, но если одна из дат была «2020-02-29», и мы пытаемся добавить к ней 1 год, используя построить dt = df['Date'].dt; pd.to_datetime(dict(year=dt.year + y, month=dt.month, ...), тогда мы получим ValueError: cannot assemble the datetimes: day is out of range for month).
  2. Он сохраняет любое время суток и информацию о часовом поясе (опять же, не в вашем случае, но в общем случае их можно было бы сохранить).

Другие вопросы по теме