НейроАгент

Как объединять Pandas DataFrames по значениям столбцов

Узнайте, как эффективно объединять несколько pandas DataFrames на основе значений столбцов в Python. Полное руководство с примерами кода для объединения DataFrames с одинаковыми значениями фруктов или категорий.

Вопрос

Как объединить несколько pandas DataFrames на основе значений столбцов в Python? У меня есть коллекция DataFrames, хранящаяся в списке с названием Data_Tables, все с идентичными столбцами. Мне нужно объединить DataFrames, которые имеют одинаковое значение в определенном столбце (например, ‘fruit’). Например, если у меня есть 10 DataFrames, из которых 3 содержат ‘apple’, 2 содержат ‘banana’, и 2 содержат ‘lemon’, я хочу создать отдельные объединенные DataFrames для каждого типа фрукта. У меня также есть списки, идентифицирующие, какие фрукты имеют несколько DataFrames (Multiple_Fruit) и их количество (Num_Dups_per_Fruit). Мой текущий подход включает вложенные циклы для определения соответствующих DataFrames, но у меня возникают проблемы с шагом объединения. Как можно эффективно объединить DataFrames на основе значений столбцов при обработке произвольного количества DataFrames и типов фруктов?

НейроАгент

Основное содержание

Базовый подход к объединению

Основой этого решения является функция concat() библиотеки pandas, которая позволяет объединять несколько DataFrame вдоль определенной оси. Когда у вас есть DataFrame с идентичными столбцами, простое объединение работает хорошо:

python
import pandas as pd

# Базовое объединение всех DataFrame
combined_df = pd.concat(Data_Tables, ignore_index=True)

Однако этот подход объединяет все DataFrame без учета значений столбца с фруктами. Для группировки по значениям фруктов требуется более сложный метод, учитывающий конкретные значения столбцов.

Группировка по значениям столбцов

Чтобы сгруппировать DataFrame по значениям столбцов, сначала нужно определить, какие DataFrame содержат каждый тип фрукта. Это можно сделать, перебирая список Data_Tables и проверяя столбец с фруктами:

python
# Словарь для хранения DataFrame по типу фрукта
fruit_groups = {}

for df in Data_Tables:
    # Получаем значение фрукта из каждого DataFrame
    fruit_value = df['fruit'].iloc[0]  # Предполагаем, что все строки в DataFrame имеют одинаковый фрукт
    
    if fruit_value not in fruit_groups:
        fruit_groups[fruit_value] = []
    fruit_groups[fruit_value].append(df)

Это создает словарь, где каждый ключ - это тип фрукта, а каждое значение - это список DataFrame, содержащих этот фрукт.

Создание отдельных DataFrame для каждого фрукта

Как только у вас есть DataFrame, сгруппированные по типу фрукта, вы можете объединить их для каждой группы:

python
# Словарь для хранения объединенных DataFrame для каждого фрукта
concatenated_fruits = {}

for fruit, dfs in fruit_groups.items():
    # Объединяем все DataFrame для этого фрукта
    concatenated_fruits[fruit] = pd.concat(dfs, ignore_index=True)

Это даст вам отдельные DataFrame для каждого типа фрукта, правильно объединенные при сохранении всех исходных данных.

Оптимизированное решение с использованием ваших списков

Учитывая, что у вас уже есть списки Multiple_Fruit и Num_Dups_per_Fruit, вы можете оптимизировать процесс, сосредоточившись только на фруктах, у которых есть несколько DataFrame:

python
def concatenate_by_fruit(data_tables, multiple_fruit, num_dups_per_fruit):
    """
    Эффективно объединяет DataFrame на основе значений столбцов.
    
    Args:
        data_tables: Список DataFrame для объединения
        multiple_fruit: Список фруктов, у которых есть несколько DataFrame
        num_dups_per_fruit: Список количества DataFrame на каждый фрукт
        
    Returns:
        Словарь объединенных DataFrame по типу фрукта
    """
    # Создаем словарь для удобного поиска количества DataFrame
    fruit_counts = dict(zip(multiple_fruit, num_dups_per_fruit))
    
    # Инициализируем словарь для хранения DataFrame по фруктам
    fruit_groups = {fruit: [] for fruit in multiple_fruit}
    
    # Обрабатываем каждый DataFrame
    for df in data_tables:
        fruit_value = df['fruit'].iloc[0]  # Получаем значение фрукта
        
        if fruit_value in fruit_counts:
            fruit_groups[fruit_value].append(df)
    
    # Объединяем DataFrame для каждого фрукта
    concatenated_results = {}
    for fruit, dfs in fruit_groups.items():
        if len(dfs) > 0:  # Объединяем только если есть DataFrame для этого фрукта
            concatenated_results[fruit] = pd.concat(dfs, ignore_index=True)
    
    return concatenated_results

Обработка крайних случаев

В вашей реализации следует учесть несколько крайних случаев:

  1. Пустые DataFrame: Обработка случаев, когда DataFrame могут быть пустыми
  2. Смешанные значения фруктов: Если DataFrame содержит несколько значений фруктов
  3. Отсутствующий столбец с фруктами: Обработка случаев, когда столбец с фруктами может отсутствовать
  4. Чувствительность к регистру: Учитывать, следует ли рассматривать ‘Apple’ и ‘apple’ как одинаковые
python
def safe_concatenate_by_fruit(data_tables, multiple_fruit, num_dups_per_fruit, fruit_column='fruit'):
    """
    Безопасная функция объединения с обработкой ошибок.
    """
    try:
        # Создаем словарь для поиска
        fruit_counts = dict(zip(multiple_fruit, num_dups_per_fruit))
        
        # Инициализируем хранилище
        fruit_groups = {fruit: [] for fruit in multiple_fruit}
        
        # Обрабатываем DataFrame с обработкой ошибок
        for df in data_tables:
            if df.empty or fruit_column not in df.columns:
                continue  # Пропускаем пустые DataFrame или те, у которых нет столбца с фруктами
                
            # Обрабатываем несколько значений фруктов, беря первое
            fruit_value = df[fruit_column].iloc[0] if not df[fruit_column].empty else None
            
            if fruit_value is not None and fruit_value in fruit_counts:
                fruit_groups[fruit_value].append(df)
        
        # Объединяем результаты
        concatenated_results = {}
        for fruit, dfs in fruit_groups.items():
            if len(dfs) > 0:
                concatenated_results[fruit] = pd.concat(dfs, ignore_index=True)
        
        return concatenated_results
        
    except Exception as e:
        print(f"Ошибка при объединении: {e}")
        return {}

Полный пример

Вот полный рабочий пример, демонстрирующий весь процесс:

python
import pandas as pd
import numpy as np

# Настройка примера данных
np.random.seed(42)

# Создание примеров DataFrame
apple_df1 = pd.DataFrame({
    'fruit': ['apple', 'apple', 'apple'],
    'color': ['red', 'green', 'yellow'],
    'weight': [150, 120, 130]
})

apple_df2 = pd.DataFrame({
    'fruit': ['apple', 'apple'],
    'color': ['red', 'green'],
    'weight': [160, 110]
})

banana_df1 = pd.DataFrame({
    'fruit': ['banana', 'banana'],
    'color': ['yellow', 'yellow'],
    'weight': [200, 210]
})

lemon_df1 = pd.DataFrame({
    'fruit': ['lemon', 'lemon', 'lemon'],
    'color': ['yellow', 'yellow', 'yellow'],
    'weight': [80, 85, 90]
})

lemon_df2 = pd.DataFrame({
    'fruit': ['lemon', 'lemon'],
    'color': ['yellow', 'yellow'],
    'weight': [75, 82]
})

# Настройка списков пользователя
Data_Tables = [apple_df1, apple_df2, banana_df1, lemon_df1, lemon_df2]
Multiple_Fruit = ['apple', 'lemon']  # Фрукты с несколькими DataFrame
Num_Dups_per_Fruit = [2, 2]  # Количество DataFrame на каждый фрукт

# Оптимизированная функция объединения
def concatenate_by_fruit_optimized(data_tables, multiple_fruit, num_dups_per_fruit):
    """
    Оптимизированная функция для объединения DataFrame по значениям фруктов.
    """
    # Создаем словарь для эффективного поиска
    fruit_to_count = dict(zip(multiple_fruit, num_dups_per_fruit))
    
    # Группируем DataFrame по фруктам
    fruit_dfs = {fruit: [] for fruit in multiple_fruit}
    
    for df in data_tables:
        if df.empty or 'fruit' not in df.columns:
            continue
            
        fruit_value = df['fruit'].iloc[0]
        
        if fruit_value in fruit_to_count:
            fruit_dfs[fruit_value].append(df)
    
    # Объединяем DataFrame для каждого фрукта
    result = {}
    for fruit, dfs in fruit_dfs.items():
        if dfs:  # Объединяем только если есть DataFrame
            result[fruit] = pd.concat(dfs, ignore_index=True)
    
    return result

# Выполняем объединение
concatenated_results = concatenate_by_fruit_optimized(Data_Tables, Multiple_Fruit, Num_Dups_per_Fruit)

# Отображаем результаты
print("Объединенные результаты:")
for fruit, df in concatenated_results.items():
    print(f"\nDataFrame для {fruit.upper()}:")
    print(df)
    print(f"Размер: {df.shape}")
    print(f"Количество исходных DataFrame: {len([d for d in Data_Tables if d['fruit'].iloc[0] == fruit])}")

Это решение эффективно обрабатывает ваши требования:

  1. Использует существующие списки Multiple_Fruit и Num_Dups_per_Fruit для целевой обработки
  2. Создает отдельные объединенные DataFrame для каждого типа фрукта
  3. Сохраняет все исходные данные, игнорируя индексы строк
  4. Корректно обрабатывает крайние случаи
  5. Предоставляет четкий, поддерживаемый код, который масштабируется с вашими данными

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

Источники

  1. Документация Pandas - concat()
  2. Документация Pandas - groupby()
  3. Документация Pandas - DataFrame.iloc

Заключение

  • Используйте группировку на основе словаря вместо вложенных циклов для лучшей производительности и читаемости
  • Используйте существующие списки (Multiple_Fruit и Num_Dups_per_Fruit) для фокусировки обработки только на фруктах с несколькими DataFrame
  • Обрабатывайте крайние случаи, такие как пустые DataFrame или отсутствующие столбцы, чтобы сделать ваше решение надежным
  • Учитывайте эффективность памяти при работе с очень большими наборами данных, обрабатывая DataFrame порциями
  • Тестируйте с примерами данных сначала, чтобы убедиться, что ваша логика объединения работает ожидаемым образом, прежде чем применять ее к полному набору данных

Этот подход обеспечивает чистое, эффективное решение, хорошо масштабируется с количеством DataFrame и типов фруктов, сохраняя целостность всех исходных данных.