Добавьте значения двух Dataframe на основе похожих значений строк

ОТРЕДАКТИРОВАНО ДЛЯ ЯСНОСТИ

Я работаю над моделью региональной электроэнергетической системы. в настоящее время у меня есть два кадра данных: один содержит информацию о муниципалитете и энергетическом узле, другой - это спрос на энергию с течением времени в муниципалитете, как показано ниже (а не фактический df, который я использую)

municip = {
    "muni_id": [1401, 1402, 1407, 1415, 1419, 1480, 1480, 1427, 1484],
    "muni_name": ["Har", "Par", "Ock", "Ste", "Tjo", "Gbg", "Gbg", "Sot", "Lys"],
    "new_muni_id": [1401, 1402, 1480, 1415, 1415, 1480, 1480, 1484, 1484],
    "new_muni_name": ["Har", "Har", "Gbg", "Ste", "Ste", "Gbg", "Gbg", "Lys", "Lys"],
    "new_node_id": ["HAR1", "PAR1", "GBG2", "STE1", "STE1", "GBG1", "GBG2", "LYS1", "LYS1"]
}

df_1 = pd.DataFrame(municip)

demand = {
    "period": [1, 2, 3, 4, 5],
    1401: [2, 4, 4, 1, 2],
    1402: [1, 1, 3, 3, 5],
    1407: [2, 4, 4, 1, 2],
    1415: [1, 1, 3, 3, 5],
    1419: [1, 1, 3, 3, 5],
    1480: [1, 1, 3, 3, 5],
    1427: [2, 4, 4, 1, 2],
    1484: [1, 2, 3, 4, 5]
}

df_2 = pd.DataFrame(demand)

есть «старый» и «новый» muni_id и имя, потому что в некоторых муниципалитетах может не быть узла, поэтому я агрегировал спрос до ближайшего узла. df_2 — это спрос во всем муниципалитете, причем число в столбцах указывает muni_id.

мое намерение состоит в том, чтобы равномерно распределить муниципальный спрос на энергетический узел (в качестве начала для простоты)

Я попробовал следующее:

  1. создать новый фрейм данных со столбцами периода и списком узлов

  2. назначьте спрос на муниципалитет, который имеет одинаковый старый и новый идентификатор/имя, разделенный на количество узлов в этом же муниципалитете, например, выше Gbg имеет 2 узла (GBG1 и GBG2), поэтому спрос на GBG1 и GBG2 будет спросом 1480 (Гбг) разделить на два.

  3. для муниципалитета, который имеет разные старый и новый идентификатор/название, требование старого идентификатора муниципалитета будет добавлено к требованию назначенного узла на основе предыдущего шага. например, в df_1 muni_id 1407 (Ock) назначен узлу GBG2, поэтому потребность GBG2 в первом периоде будет равна 2,5, где 0,5 — это предыдущий шаг (потребность Gbg / 2), 2 — текущий шаг (потребность Ок)

import pandas as pd

def profiling(df_1, df_2):
    
    # create empty df
    nodes = df_1["new_node_id"].unique()
    node_df = pd.DataFrame(columns=["period"] + list(nodes))
    node_df["period"] = df_2["period"]

    # loop based on node id
    for node in nodes:
        node_info = df_1[df_1["new_node_id"] == node]
        muni_id = node_info["muni_id"].values[0]
        new_muni_id = node_info["new_muni_id"].values[0]

        # assign demand to all nodes that has same old/new id
        if muni_id == new_muni_id :
            cumul_node = len(df_1[df_1["muni_id"] == muni_id])
            node_df[node] = df_2[muni_id] / cumul_node 
        
        # assign demand to node for muni that has different old/new id
        else:
            cumul_node = len(df_1[df_1["new_muni_id"] == new_muni_id])         
            add_demand = df_2[muni_id] / cumul_node

            if node in node_df:
                node_df[node] += add_demand
            else:
                node_df[node] = add_demand

    return node_df

Однако это приводит к тому, что некоторые узлы возвращают значения NaN, особенно муниципалитеты, которые имеют разные старые/новые идентификаторы, как показано в таблице ниже.

период ХАР1 ПАР1 ГБГ2 СТЭ1 ГБГ1 ЛИС1 1 2 1 НЭН 1 0,5 НЭН 2 4 1 НЭН 1 0,5 НЭН 3 4 3 НЭН 3 1,5 НЭН 4 1 3 НЭН 3 1,5 НЭН 5 2 5 НЭН 5 2,5 НЭН

по моей логике, почасовая потребность GBG2 должна составлять половину от 1480 (Gbg muni) + 1407 (Ock muni), поскольку Ock назначается GBG2 как новый узел, т.е. 2,5 в первый период. То же самое касается узла LYS1, где общая потребность составит 1427 (Sot) и 1484 (Lys).

Поэтому я ожидаю получить что-то вроде этого

период ХАР1 ПАР1 ГБГ2 СТЭ1 ГБГ1 ЛИС1 1 2 1 2,5 1 0,5 3 2 4 1 4,5 1 0,5 6 3 4 3 5,5 3 1,5 7 4 1 3 2,5 3 1,5 5 5 2 5 4,5 5 2,5 7

Может ли кто-нибудь улучшить мою функцию, или, возможно, в моей логике добавления значений есть ошибка?

Извините за длинный вопрос и заранее спасибо за помощь!

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

furas 24.06.2024 23:24

спасибо за предложение! отредактирую вопрос

pprianto 25.06.2024 09:57
Почему в 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
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Я запутался, пытаясь решить эту проблему с помощью фрагмента кода, который вы предоставили для циклов, поэтому вот мой подход:

def profiling(df_1, df_2):

    df_1['num_nodes'] = df_1.groupby('muni_id')['new_node_id'].transform('count')

    df_2_melted = df_2.melt(id_vars=['period'], var_name='muni_id', value_name='demand')

    merged_df = df_2_melted.merge(df_1, on='muni_id', how='left')

    merged_df['demand'] = merged_df['demand'] / merged_df['num_nodes']

    final_df = merged_df.pivot_table(index='period', columns='new_node_id', values='demand', aggfunc='sum').reset_index()

    final_df = final_df.rename_axis(None, axis=1)

    return final_df

Если мы разберем код:

1-й шаг

df_1['num_nodes'] = df_1.groupby('muni_id')['new_node_id'].transform('count')

Это подсчитывает количество узлов в каждой группе узлов muni_id и возвращает Series, присвоенный столбцу num_nodes.

2-й шаг

df_2_melted = df_2.melt(id_vars=['period'], var_name='muni_id', value_name='demand')

Использование melt создает новый DataFrame, в который добавляется столбец с именем muni_id для переключения с таблицы n_period x n_muni на один столбец значений для спроса за период для данного узла. Это означает, что каждая строка соответствует определенной паре period и muni_id с соответствующим спросом.

3-й шаг

merged_df = df_2_melted.merge(df_1, on='muni_id', how='left')

Это объединяет df_2_melted с df_1 с помощью столбца muni_id. Это добавит всю информацию из df_1 в df_2_melted для каждого period, muni_id и demand.

4-й шаг

merged_df['demand'] = merged_df['demand'] / merged_df['num_nodes']

Это делит спрос на количество узлов, найденных на шаге 1.

5-й шаг

final_df = merged_df.pivot_table(index='period', columns='new_node_id', values='demand', aggfunc='sum').reset_index()

При этом создается сводная таблица, в которой period в качестве строк и node_id в качестве столбцов. Таблица заполнена значениями из столбца demand, и чтобы обеспечить правильную обработку старых и новых идентификаторов муниципалитетов, используется aggfunc='sum'. .reset_index() позволяет превратить индекс в столбец period.

6-й шаг

final_df = final_df.rename_axis(None, axis=1)

Удаляет имя new_node_id индекса, поскольку оно больше не является этим.

Проверка

Используя приведенный вами пример данных, я получаю:

municip = {
    "muni_id": [1401, 1402, 1407, 1415, 1419, 1480, 1480, 1427, 1484],
    "muni_name": ["Har", "Par", "Ock", "Ste", "Tjo", "Gbg", "Gbg", "Sot", "Lys"],
    "new_muni_id": [1401, 1402, 1480, 1415, 1415, 1480, 1480, 1484, 1484],
    "new_muni_name": ["Har", "Har", "Gbg", "Ste", "Ste", "Gbg", "Gbg", "Lys", "Lys"],
    "new_node_id": ["HAR1", "PAR1", "GBG2", "STE1", "STE1", "GBG1", "GBG2", "LYS1", "LYS1"]
}
df_1 = pd.DataFrame(municip)

demand = {
    "period": [1, 2, 3, 4, 5],
    1401: [2, 4, 4, 1, 2],
    1402: [1, 1, 3, 3, 5],
    1407: [2, 4, 4, 1, 2],
    1415: [1, 1, 3, 3, 5],
    1419: [1, 1, 3, 3, 5],
    1480: [1, 1, 3, 3, 5],
    1427: [2, 4, 4, 1, 2],
    1484: [1, 2, 3, 4, 5]
}
df_2 = pd.DataFrame(demand)


profiling(df_1, df_2)

>>>
   period  GBG1  GBG2  HAR1  LYS1  PAR1  STE1
0       1   0.5   2.5   2.0   3.0   1.0   2.0
1       2   0.5   4.5   4.0   6.0   1.0   2.0
2       3   1.5   5.5   4.0   7.0   3.0   6.0
3       4   1.5   2.5   1.0   5.0   3.0   6.0
4       5   2.5   4.5   2.0   7.0   5.0  10.0

Это здорово и намного короче того, что у меня есть! Спасибо! В будущих случаях мне следовало бы больше внимания уделять поворотным таблицам!

pprianto 25.06.2024 10:59

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

С помощью Python извлеките в файл Excel значение ячейки в строке, где ячейка в той же строке содержит строку символов из XML-файла
Обмен датой начала, датой окончания и другими столбцами с более ранней строкой, если даты больше 8 в фрейме данных pandas
Как я могу сравнить значение в одном столбце со всеми значениями, которые находятся ДО него в другом столбце, чтобы найти количество уникальных значений, которые меньше?
Присоединиться к фрейму данных с двойной записью
Получите минимум за счет итераций записи в фрейме данных pandas
Добавьте количество строк в виде списка в столбец, используя groupby
Сохраните данные на новой вкладке в файле .xlsx
Pandas groupby — группировать столбцы в список количества значений строк
Как изменить формат номеров столбцов df на местную валюту
Как эффективно сравнить два кадра данных и получить значение столбца на основе условия