Как объединить несколько 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 и типов фруктов?
Основное содержание
- Базовый подход к объединению
- Группировка по значениям столбцов
- Создание отдельных DataFrame для каждого фрукта
- Оптимизированное решение с использованием ваших списков
- Обработка крайних случаев
- Полный пример
Базовый подход к объединению
Основой этого решения является функция concat() библиотеки pandas, которая позволяет объединять несколько DataFrame вдоль определенной оси. Когда у вас есть DataFrame с идентичными столбцами, простое объединение работает хорошо:
import pandas as pd
# Базовое объединение всех DataFrame
combined_df = pd.concat(Data_Tables, ignore_index=True)
Однако этот подход объединяет все DataFrame без учета значений столбца с фруктами. Для группировки по значениям фруктов требуется более сложный метод, учитывающий конкретные значения столбцов.
Группировка по значениям столбцов
Чтобы сгруппировать DataFrame по значениям столбцов, сначала нужно определить, какие DataFrame содержат каждый тип фрукта. Это можно сделать, перебирая список Data_Tables и проверяя столбец с фруктами:
# Словарь для хранения 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, сгруппированные по типу фрукта, вы можете объединить их для каждой группы:
# Словарь для хранения объединенных 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:
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
Обработка крайних случаев
В вашей реализации следует учесть несколько крайних случаев:
- Пустые DataFrame: Обработка случаев, когда DataFrame могут быть пустыми
- Смешанные значения фруктов: Если DataFrame содержит несколько значений фруктов
- Отсутствующий столбец с фруктами: Обработка случаев, когда столбец с фруктами может отсутствовать
- Чувствительность к регистру: Учитывать, следует ли рассматривать ‘Apple’ и ‘apple’ как одинаковые
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 {}
Полный пример
Вот полный рабочий пример, демонстрирующий весь процесс:
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])}")
Это решение эффективно обрабатывает ваши требования:
- Использует существующие списки
Multiple_FruitиNum_Dups_per_Fruitдля целевой обработки - Создает отдельные объединенные DataFrame для каждого типа фрукта
- Сохраняет все исходные данные, игнорируя индексы строк
- Корректно обрабатывает крайние случаи
- Предоставляет четкий, поддерживаемый код, который масштабируется с вашими данными
Подход устраняет необходимость во вложенных циклах, используя группировку на основе словаря, что делает его как эффективным, так и легким для понимания.
Источники
Заключение
- Используйте группировку на основе словаря вместо вложенных циклов для лучшей производительности и читаемости
- Используйте существующие списки (
Multiple_FruitиNum_Dups_per_Fruit) для фокусировки обработки только на фруктах с несколькими DataFrame - Обрабатывайте крайние случаи, такие как пустые DataFrame или отсутствующие столбцы, чтобы сделать ваше решение надежным
- Учитывайте эффективность памяти при работе с очень большими наборами данных, обрабатывая DataFrame порциями
- Тестируйте с примерами данных сначала, чтобы убедиться, что ваша логика объединения работает ожидаемым образом, прежде чем применять ее к полному набору данных
Этот подход обеспечивает чистое, эффективное решение, хорошо масштабируется с количеством DataFrame и типов фруктов, сохраняя целостность всех исходных данных.