Обмен датой начала, датой окончания и другими столбцами с более ранней строкой, если даты больше 8 в фрейме данных pandas

Данные:

# Sample data
data = {
    'Start Date': ['2022-10-18', '2022-10-25', '2023-04-17'],
    'End Date': ['2022-10-20', '2023-04-06', '2023-07-04'],
    'Close1': [17486.95, 17656.35, 17706.85],
    'Close2': [17563.95, 17599.15, 19389.00],
    'NF_BEES1': [0.58, 0.19, 0.12],
    'NF_BEES2': [0.63, 0.75, 0.73],
    'Difference': [77.00, -57.20, 1682.15],
    'Days Difference': [2, 163, 78]
}

# Create DataFrame
df = pd.DataFrame(data)
df

# Convert date columns to datetime
df['Start Date'] = pd.to_datetime(df['Start Date'])
df['End Date'] = pd.to_datetime(df['End Date'])

Выход:

    Start Date  End Date    Close1  Close2  NF_BEES1    NF_BEES2    Difference  Days Difference
0   2022-10-18  2022-10-20  17486.95    17563.95    0.58    0.63    77.00   2
1   2022-10-25  2023-04-06  17656.35    17599.15    0.19    0.75    -57.20  163
2   2023-04-17  2023-07-04  17706.85    19389.00    0.12    0.73    1682.15 78

Если столбец Days Difference > 8, то создайте новую строку в df, и End Date предыдущей строки должно быть Start Date в новой строке, а Start Date текущей строки (разница в днях > 8) должно быть End Date.

Значения Close1, Close2, NF_BEES1, NF_BEES2 также следует изменить, например Start Date и End Date. Исходя из этого, необходимо рассчитать Days Difference для новой строки.

Мой код (не работает должным образом):

# Iterate through the DataFrame
for idx, row in df.iterrows():
    rows.append(row)
    if row['Days Difference'] > 8:
        # Create a new row
        new_row = row.copy()
        new_row['Start Date'] = rows[-2]['End Date']
        new_row['Close1'] = rows[-2]['Close2']
        new_row['NF_BEES1'] = rows[-2]['NF_BEES2']
        new_row['End Date'] = row['Start Date']
        new_row['Close2'] = row['Close1']
        new_row['NF_BEES2'] = row['NF_BEES1']
        new_row['Difference'] = (new_row['Close2'] - new_row['Close1'])
        new_row['Days Difference'] = (new_row['End Date'] - new_row['Start Date']).days
        print(new_row)
        print(type(new_row))

# Create a new DataFrame with the split rows
new_df = pd.DataFrame(rows)
new_df

Мой результат:


Start Date  End Date    Close1  Close2  NF_BEES1    NF_BEES2    Difference  Days Difference
0   2022-10-18  2022-10-20  17486.95    17563.95    0.58    0.63    77.00   2
1   2022-10-25  2023-04-06  17656.35    17599.15    0.19    0.75    -57.20  163
2   2023-04-17  2023-07-04  17706.85    19389.00    0.12    0.73    1682.15 78

Ожидаемый результат:

Start Date  End Date    Close1  Close2  NF_BEES1    NF_BEES2    Difference  Days Difference
18  2022-10-18  2022-10-20  17486.95    17563.95    0.58    0.63    77.00   2
19  2022-10-20  2022-10-25  17563.95    17656.35    0.63    0.19    93  5
20  2023-04-06  2023-04-17  17599.15    17706.85    0.75    0.12    107 11

В своем вопросе вы не упомянули судьбу Close/NF_BEES/Difference. Хотя это можно сделать из вывода, лучше отредактируйте вопрос, чтобы он был явным;)

mozway 24.06.2024 15:58

Добавлена ​​информация в запрос. Спасибо @mozway, @e-motta!!

Divyank 24.06.2024 16:05
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

IIUC, используйте маску, копию End и сдвиг:

# identify rows to change
m = df['Days Difference'].gt(8)

# keep a copy of the previous End
prev_end = df['End Date'].shift()

# update the rows
df.loc[m, 'End Date'] = df['Start Date']
df.loc[m, 'Start Date'] = prev_end

Выход:

  Start Date   End Date    Close1    Close2  NF_BEES1  NF_BEES2  Difference  Days Difference
0 2022-10-18 2022-10-20  17486.95  17563.95      0.58      0.63       77.00                2
1 2022-10-20 2022-10-25  17656.35  17599.15      0.19      0.75      -57.20              163
2 2023-04-06 2023-04-17  17706.85  19389.00      0.12      0.73     1682.15               78

обобщение

Если вам нужен новый DataFrame в качестве вывода, просто сделайте то же самое с копией, а если вы хотите обновить несколько столбцов, примените shift ко всем сразу и обновите различия, а затем выполните вычитание (и преобразуйте в дней, когда нужный):

# make a copy
new_df = df.copy()

# shift the columns
m = df['Days Difference'].gt(8)
new_df.loc[m, ['End Date', 'Close2', 'NF_BEES2']] = df[['Start Date', 'Close1', 'NF_BEES1']][m].values
new_df.loc[m, ['Start Date', 'Close1', 'NF_BEES1']] = df[['End Date', 'Close2', 'NF_BEES2']].shift()[m].values

# update the differences
new_df['Difference'] = new_df['Close2'].sub(new_df['Close1'])
new_df['Days Difference'] = new_df['End Date'].sub(new_df['Start Date']).dt.days

Выход:

  Start Date   End Date    Close1    Close2  NF_BEES1  NF_BEES2  Difference  Days Difference
0 2022-10-18 2022-10-20  17486.95  17563.95      0.58      0.63        77.0                2
1 2022-10-20 2022-10-25  17563.95  17656.35      0.63      0.19        92.4                5
2 2023-04-06 2023-04-17  17599.15  17706.85      0.75      0.12       107.7               11

IIUC, вам также потребуется изменить столбцы 'Close...' и 'NF_BEES...'.

e-motta 24.06.2024 15:50

@e-motta, конечно, это та же логика, хотя в вопросе это не объяснено.

mozway 24.06.2024 15:52

Правда, я сделал это из названия и ожидаемого результата, но в самом вопросе об этом не упоминается.

e-motta 24.06.2024 15:53

@e-motta Я добавил полную логику, попрошу ОП разъяснить это

mozway 24.06.2024 15:57
Ответ принят как подходящий

Предполагая, что ваши столбцы отсортированы в определенном порядке 'Start Date', 'End Date', 'Close1', 'Close2', 'NF_BEES1', 'NF_BEES2', вы можете использовать shift условно так:

out = df.copy()

mask = df["End Date"] - df["Start Date"] > pd.Timedelta(days=8)

out.loc[mask, ["Start Date", "Close1", "NF_BEES1"]] = df.shift().shift(-1, axis=1)
out.loc[mask, ["End Date", "Close2", "NF_BEES2"]] = df.shift(axis=1)

out["Difference"] = out["Close2"] - out["Close1"]
out["Days Difference"] = (out["End Date"] - out["Start Date"]).dt.days
  Start Date   End Date    Close1    Close2  NF_BEES1  NF_BEES2  Difference  Days Difference
0 2022-10-18 2022-10-20  17486.95  17563.95      0.58      0.63        77.0                2
1 2022-10-20 2022-10-25  17563.95  17656.35      0.63      0.19        92.4                5
2 2023-04-06 2023-04-17  17599.15  17706.85      0.75      0.12       107.7               11

со многими shift кажется немного сложным, не так ли? ;)

mozway 24.06.2024 15:40

@mozway Возможно, да, но я не мог придумать ничего проще. :-)

e-motta 24.06.2024 15:49

Это хороший способ сделать его кратким, но вам следует просто упомянуть, что для этого необходимо, чтобы столбцы были отсортированы в определенном порядке A1/A2/B1/B2/C1/C2, иначе это приведет к путанице значений.

mozway 24.06.2024 16:02

@mozway Хорошая мысль, я добавил комментарий по этому поводу.

e-motta 24.06.2024 16:12

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