Я все еще новичок в питонах. Я пытаюсь создать код Python, чтобы получить дату извлечения, в которой впервые произошел разрыв, как start_time. Когда условие становится «Y», я хотел бы создать end_date на основе условия и заполнить дату извлечения, в которой оно стало «Y». В то же время я хочу сверить его с предыдущими датами, прежде чем указывать дату окончания. Например, гэп открылся в январе и закрылся в феврале, но вновь открылся в марте и закрылся в апреле. В целом, я хочу проводить непрерывную проверку на случай, если в будущем добавлю больше данных.
Пример таблицы данных:
В целом, это то, что я ожидаю:
Пример повторного открытия пробела происходит для идентификатора 100 с sub_id, равным x, тогда как непрерывный разрыв происходит для идентификатора 101 с sub_id, равным z.
🤔 А знаете ли вы, что...
В Python есть инструменты для тестирования кода, такие как библиотека unittest.
Пытаться:
def fn(x):
start_dates, end_dates = [], []
start_date = None
for c, d in zip(x["Condition"], x["extraction_date"]):
if start_date is None:
start_date = d
start_dates.append(start_date)
if c == "Y":
end_dates.append(d)
start_date = None
else:
end_dates.append(None)
x["start_date"] = start_dates
x["end_dates"] = end_dates
return x
df = (
df.groupby(["ID", "sub_id"], group_keys=True)
.apply(fn, include_groups=False)
.sort_index(level=2)
.droplevel(2)
.reset_index()
)
print(df)
Распечатки:
ID sub_id Condition extraction_date start_date end_dates
0 100 x N 2024-01-15 2024-01-15 None
1 100 x Y 2024-02-01 2024-01-15 2024-02-01
2 100 y Y 2024-02-01 2024-02-01 2024-02-01
3 101 z N 2024-02-01 2024-02-01 None
4 101 z N 2024-03-16 2024-02-01 None
5 100 x N 2024-03-16 2024-03-16 None
6 100 x Y 2024-04-10 2024-03-16 2024-04-10
7 101 z N 2024-04-10 2024-02-01 None
8 101 z Y 2024-05-15 2024-02-01 2024-05-15
9 102 w N 2024-05-15 2024-05-15 None
10 102 w Y 2024-06-15 2024-05-15 2024-06-15
Вот способ решить вашу проблему и узнать, когда каждый "gap"
начинается (Condition == 'N')
и когда заканчивается (Condition == 'Y')
. Я придерживаюсь базового Python, не полагаясь на панды, которые действительно были бы лучшим выбором во многих сценариях с реальными данными.
Шаг 1: Сначала импортируйте дату и время для обработки этих дат. (вы не хотите возиться со строками даты)
from datetime import datetime
Шаг 2: Вот ваши данные в виде списка словарей (похож на JSON, но на Python).
data = [
{'ID': 100, 'sub_id': 'x', 'Condition': 'N', 'extraction_date': '2024-01-15'},
{'ID': 100, 'sub_id': 'x', 'Condition': 'Y', 'extraction_date': '2024-02-01'},
{'ID': 100, 'sub_id': 'y', 'Condition': 'Y', 'extraction_date': '2024-02-01'},
{'ID': 101, 'sub_id': 'z', 'Condition': 'N', 'extraction_date': '2024-02-01'},
{'ID': 101, 'sub_id': 'z', 'Condition': 'N', 'extraction_date': '2024-03-16'},
{'ID': 100, 'sub_id': 'x', 'Condition': 'N', 'extraction_date': '2024-03-16'},
{'ID': 100, 'sub_id': 'x', 'Condition': 'Y', 'extraction_date': '2024-04-10'},
{'ID': 101, 'sub_id': 'z', 'Condition': 'N', 'extraction_date': '2024-04-10'},
{'ID': 101, 'sub_id': 'z', 'Condition': 'Y', 'extraction_date': '2024-05-15'},
{'ID': 102, 'sub_id': 'w', 'Condition': 'N', 'extraction_date': '2024-05-15'},
{'ID': 102, 'sub_id': 'w', 'Condition': 'Y', 'extraction_date': '2024-06-15'},
]
Шаг 3: Преобразуйте строки Extract_date в объекты DateTime.
for entry in data:
entry['extraction_date'] = datetime.strptime(entry['extraction_date'], '%Y-%m-%d')
шаг 4: Используйте словарь start_tracker, чтобы отслеживать, где начинается и заканчивается пробел.
start_tracker = {}
Шаг 5: Просмотрите каждую запись и выясните, начинаете ли вы новый пробел, завершаете существующий или просто расслабляетесь между ними.
for entry in data:
key = (entry['ID'], entry['sub_id'])
if entry['Condition'] == 'N':
# new gap or continuation of existing gap
if key not in start_tracker or start_tracker[key]['end_date'] is not None:
# start new gap
start_tracker[key] = {'start_date': entry['extraction_date'], 'end_date': None}
# Update current entry with the start date
entry['start_date'] = start_tracker[key]['start_date']
entry['end_date'] = None
elif entry['Condition'] == 'Y':
# end gap
if key in start_tracker and start_tracker[key]['end_date'] is None:
# update end date for this gap
start_tracker[key]['end_date'] = entry['extraction_date']
entry['start_date'] = start_tracker[key]['start_date']
entry['end_date'] = entry['extraction_date']
else:
# Edege case handling (not expected in your data)
entry['start_date'] = None
entry['end_date'] = entry['extraction_date']
else:
# if we get an unexpected condition then we handle it gracefully
entry['start_date'] = None
entry['end_date'] = None
Шаг 6: Отобразите результаты
print("ID", "sub_id", "Condition", "extraction_date", "start_date", "end_date", sep = "\t")
for entry in data:
print(
entry['ID'], entry['sub_id'], entry['Condition'],
entry['extraction_date'].strftime('%Y-%m-%d'),
entry['start_date'].strftime('%Y-%m-%d') if entry['start_date'] else "N/A",
entry['end_date'].strftime('%Y-%m-%d') if entry['end_date'] else "N/A", sep = "\t"
)
Запуск кода даст вам:
(venv) PS C:\Users\xxxxxxx\xxxx\src> python .\test.py
ID sub_id Condition extraction_date start_date end_date
100 x N 2024-01-15 2024-01-15 N/A
100 x Y 2024-02-01 2024-01-15 2024-02-01
100 y Y 2024-02-01 N/A 2024-02-01
101 z N 2024-02-01 2024-02-01 N/A
101 z N 2024-03-16 2024-02-01 N/A
100 x N 2024-03-16 2024-03-16 N/A
100 x Y 2024-04-10 2024-03-16 2024-04-10
101 z N 2024-04-10 2024-02-01 N/A
101 z Y 2024-05-15 2024-02-01 2024-05-15
102 w N 2024-05-15 2024-05-15 N/A
102 w Y 2024-06-15 2024-05-15 2024-06-15
Отсортируйте значения по идентификатору и sub_id и используйте условие «Условие == Y» для создания групп. После этого сгруппируйте и используйте преобразование, чтобы получить даты для столбцов. Используйте панды, где с первым условием (Условие == Y) удалите ненужные даты.
m = df.sort_values(by=['ID', 'sub_id'])['Condition'].eq('Y')
g = m[::-1].cumsum().sort_index()
grp = df.groupby(['ID', 'sub_id', g])['extraction_date']
df['start_date'] = grp.transform('min')
df['end_date'] = grp.transform('max').where(m)
Конечный результат:
ID sub_id Condition extraction_date start End
100 x N 2024-01-15 2024-01-15 NaT
100 x Y 2024-02-01 2024-01-15 2024-02-01
100 y Y 2024-02-01 2024-02-01 2024-02-01
101 z N 2024-02-01 2024-02-01 NaT
101 z N 2024-03-16 2024-02-01 NaT
100 x N 2024-03-16 2024-03-16 NaT
100 x Y 2024-04-10 2024-03-16 2024-04-10
101 z N 2024-04-10 2024-02-01 NaT
101 z Y 2024-05-15 2024-02-01 2024-05-15
102 w N 2024-05-15 2024-05-15 NaT
102 w Y 2024-06-15 2024-05-15 2024-06-15
Обновлено: слегка измененная версия на случай, если группы формируются неправильно с использованием первого решения.
m = df['Condition'].eq('Y')
g = (df.groupby(['ID', 'sub_id'])['Condition']
.transform(lambda g: g[::-1].eq('Y').cumsum())
.to_numpy() # or reset_index(drop=True)
)
grps = df.groupby(['ID', 'sub_id', g])['extraction_date']
df['start_date'] = grps.transform('min')
df['end_date'] = grps.transform('max').where(m)