Другое

Индикатор объединения в Polars: Полное руководство

Узнайте, как добавлять индикаторные столбцы к операциям объединения в Polars для отслеживания совпавших и несовпавших строк, как в pandas и STATA. Полное руководство с примерами кода и решениями проблем.

Как получить аналогичный результат с помощью polars? При объединении двух таблиц в STATA выводится количество объединенных и не объединенных строк. Например, при использовании 1:1 объединения по столбцу ‘make’ между таблицами autosize и autoexpense, STATA показывает, сколько строк было сопоставлено из каждого набора данных. Я ищу способ отображения совпавших и несовпавших строк в операции объединения Polars, аналогично тому, как работает функция merge в pandas с параметром indicator.

Polars: Работа с индикаторами при объединении данных

В настоящее время Polars не имеет встроенной опции индикатора, подобной функции merge() в pandas, для автоматического отслеживания совпавших и несопоставленных строк. Однако существует несколько обходных путей, которые можно реализовать для достижения аналогичного функционала и отслеживания происхождения строк после операции объединения.


Содержание


Текущее состояние поддержки индикаторов в Polars

На текущую версию Polars не поддерживает нативно индикаторные столбцы в операциях объединения. Это была запрошенная функция, где пользователи хотели получить “индикатор, опцию validate в объединении dataframe” для проверки совпавших и несопоставленных строк после объединения.

В отличие от pandas, который предоставляет параметр indicator=True в merge(), метод join() Polars ориентирован на производительность и не автоматически отслеживает происхождение строк. В документации Polars упоминаются различные стратегии объединения, но не включена встроенная функциональность индикаторов.


Обходной путь 1: Создание индикаторного столбца вручную

Вы можете вручную создавать индикаторные столбцы, определяя, какие строки происходят из какого набора данных:

python
import polars as pl

# Пример данных
df1 = pl.DataFrame({
    'make': ['Toyota', 'Honda', 'Ford'],
    'size': ['medium', 'small', 'large']
})

df2 = pl.DataFrame({
    'make': ['Toyota', 'Honda', 'BMW'],
    'expense': [25000, 18000, 35000]
})

# Выполнение left join
result = df1.join(df2, on='make', how='left')

# Добавление индикаторного столбца вручную
result = result.with_columns(
    pl.when(pl.col('expense').is_null())
    .then('left_only')
    .when(pl.col('make').is_in(df2['make']))
    .then('both')
    .otherwise('right_only')
    .alias('merge_indicator')
)

print(result)

Этот подход создает индикаторный столбец, который показывает:

  • ‘both’ для совпавших строк
  • ‘left_only’ для строк только в левом dataframe
  • ‘right_only’ для строк только в правом dataframe

Обходной путь 2: Пост-объединительный анализ

Другой подход - анализировать результат объединения для подсчета совпавших и несопоставленных строк:

python
def analyze_join_result(left_df, right_df, join_result, join_type='left'):
    """
    Анализ результата объединения для отображения количества совпавших/несопоставленных строк
    """
    # Подсчет уникальных значений в ключевом столбце для каждого dataframe
    left_unique = left_df.select('make').n_unique()
    right_unique = right_df.select('make').n_unique()
    
    # Подсчет совпавших строк (не-null в столбцах правого dataframe)
    if join_type in ['left', 'inner', 'full']:
        matched_rows = join_result.filter(
            ~pl.col('expense').is_null()
        ).height
    else:
        matched_rows = 0
    
    # Расчет несопоставленных строк на основе типа объединения
    if join_type == 'left':
        unmatched_left = left_unique - matched_rows
        unmatched_right = 0
    elif join_type == 'right':
        unmatched_left = 0
        unmatched_right = right_unique - matched_rows
    elif join_type == 'full':
        unmatched_left = left_unique - matched_rows
        unmatched_right = right_unique - matched_rows
    else:  # inner
        unmatched_left = left_unique - matched_rows
        unmatched_right = right_unique - matched_rows
    
    # Отображение результатов, аналогично выводу STATA
    print(f"Тип объединения: {join_type.upper()}")
    print(f"Строк из левого набора данных: {left_unique}")
    print(f"Строк из правого набора данных: {right_unique}")
    print(f"Совпавших строк: {matched_rows}")
    print(f"Несопоставленных строк из левого: {unmatched_left}")
    print(f"Несопоставленных строк из правого: {unmatched_right}")
    print(f"Всего строк в результате: {join_result.height}")

# Использование
result = df1.join(df2, on='make', how='left')
analyze_join_result(df1, df2, result, 'left')

Обходной путь 3: Пользовательская функция объединения

Создайте комплексную функцию объединения, которая предоставляет подробный вывод, аналогичный STATA:

python
def smart_join(left_df, right_df, on=None, how='left', **kwargs):
    """
    Расширенная функция объединения с отслеживанием строк и анализом
    """
    # Выполнение объединения
    result = left_df.join(right_df, on=on, how=how, **kwargs)
    
    # Получение имен столбцов из правого dataframe (исключая ключи объединения)
    right_cols = [col for col in right_df.columns if col not in (on if isinstance(on, list) else [on])]
    
    # Создание индикаторного столбца
    if how == 'left':
        indicator = pl.when(pl.col(right_cols[0]).is_null()).then('left_only').otherwise('both')
    elif how == 'right':
        indicator = pl.when(pl.col(right_cols[0]).is_null()).then('right_only').otherwise('both')
    elif how == 'full':
        indicator = pl.when(
            pl.col(right_cols[0]).is_null() & pl.col(on if isinstance(on, list) else [on]).is_not_null()
        ).then('left_only').when(
            pl.col(right_cols[0]).is_not_null() & pl.col(on if isinstance(on, list) else [on]).is_null()
        ).then('right_only').otherwise('both')
    else:  # inner
        indicator = pl.lit('both')
    
    result = result.with_columns(indicator.alias('merge_indicator'))
    
    # Выполнение анализа
    left_count = left_df.height
    right_count = right_df.height
    matched_count = result.filter(pl.col('merge_indicator') == 'both').height
    unmatched_left = result.filter(pl.col('merge_indicator') == 'left_only').height
    unmatched_right = result.filter(pl.col('merge_indicator') == 'right_only').height
    
    # Вывод анализа
    print(f"\n{'='*50}")
    print(f"АНАЛИЗ ОБЪЕДИНЕНИЯ - {how.upper()} JOIN")
    print(f"{'='*50}")
    print(f"Строк в левом наборе данных: {left_count}")
    print(f"Строк в правом наборе данных: {right_count}")
    print(f"Совпавших строк: {matched_count}")
    print(f"Несопоставленных строк из левого: {unmatched_left}")
    print(f"Несопоставленных строк из правого: {unmatched_right}")
    print(f"Всего строк в результате: {result.height}")
    print(f"{'='*50}\n")
    
    return result

# Использование
result = smart_join(df1, df2, on='make', how='left')

Сравнение с Pandas и STATA

Объединение в Pandas с индикатором

python
# Подход с pandas
import pandas as pd

df1_pd = df1.to_pandas()
df2_pd = df2.to_pandas()

pandas_result = pd.merge(
    df1_pd, df2_pd, 
    on='make', 
    how='left', 
    indicator=True
)

print(pandas_result['_merge'].value_counts())

Вывод объединения в STATA

STATA предоставляет подробный вывод, показывающий:

  • Количество наблюдений из основного набора данных
  • Количество наблюдений из используемого набора данных
  • Количество совпадений
  • Количество несопоставленных строк из каждого набора данных

Сравнение с Polars

Polars в настоящее время требует ручной реализации этой функциональности, но предлагает:

  • Лучшую производительность для больших наборов данных
  • Более гибкие операции объединения
  • Лучшую эффективность использования памяти
  • Более чистый API для сложных операций

Будущие разработки в Polars

Сообщество Polars признало необходимость функциональности индикаторов. Проблема на GitHub #5983 специально запрашивает:

“Индикатор полезен, когда нужно проверить совпавшие строки против несопоставленных после объединения”

Также есть проблема #2377, запрашивающая:

“Добавить необязательный валидационный вывод к объединениям”

Предлагаемая функция будет включать:

  • Параметр indicator=True, аналогичный pandas
  • Опцию validate для валидации типа объединения
  • Автоматическое отслеживание происхождения строк

Это указывает на то, что будущие версии Polars могут включить нативную поддержку индикаторных столбцов, что сделает обходные пути ненужными.


Источники

  1. GitHub Issue #5983: Add indicator, validate option in dataframe join
  2. Stack Overflow: Show matched rows in polars join
  3. Polars User Guide: Joins
  4. Polars Documentation: DataFrame.join
  5. Stack Overflow: Get indicator column when using df.join
  6. GitHub Issue #2377: Add optional validation output to joins

Заключение

Хотя Polars в настоящее время не имеет встроенной функциональности индикаторов, как в pandas или STATA, вы можете достичь аналогичных результатов через несколько обходных путей:

  1. Ручные индикаторные столбцы - Создайте собственную логику индикатора с помощью условных выражений Polars
  2. Пост-объединительный анализ - Подсчитывайте совпавшие и несопоставленные строки после выполнения объединения
  3. Пользовательская функция объединения - Постройте комплексное решение, предоставляющее подробный анализ совпавших и несопоставленных строк

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

Компромисс заключается в том, что Polars отдает приоритет производительности и гибкости вместо удобных функций, но для большинства случаев использования обходные пути достаточны и сохраняют отличные характеристики производительности Polars.

Авторы
Проверено модерацией
Модерация