Перейти к содержимому

Объединение таблиц

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

В DataFrame есть два основных способа объединения:

  • DataFrame.Concat(...) — добавить строки одной таблицы к строкам другой;
  • Join(...) — соединить строки разных таблиц по общему ключу.

Оба способа возвращают новый DataFrame и не изменяют исходные таблицы.

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

uses MLABC;
begin
var september := DataFrame.FromCsvText('''
Имя,Предмет,Балл
Анна,Математика,91
Борис,Математика,76
''');
var october := DataFrame.FromCsvText('''
Имя,Предмет,Балл
Вера,Математика,84
Глеб,Математика,68
''');
var allMarks := DataFrame.Concat(september, october);
allMarks.Print;
end.

Вывод:

Имя Предмет Балл
Анна Математика 91
Борис Математика 76
Вера Математика 84
Глеб Математика 68

Для Concat схемы таблиц должны совпадать: имена столбцов, порядок, типы и categorical-флаги.

Join используется, когда в разных таблицах есть общий ключевой столбец. Например, в одной таблице хранятся ученики и классы, а в другой — баллы.

uses MLABC;
begin
var students := DataFrame.FromCsvText('''
Имя,Класс
Анна,8А
Борис,8Б
Вера,8А
''');
var marks := DataFrame.FromCsvText('''
Имя,Балл
Анна,91
Борис,76
Глеб,68
''');
var res := students.Join(marks, 'Имя');
res.Print;
end.

По умолчанию используется inner join: в результат попадают только строки, для которых ключ найден в обеих таблицах. В примере Вера есть только в таблице students, а Глеб — только в таблице marks, поэтому они не попадут в результат обычного Join.

Если нужно сохранить все строки из первой таблицы, используется jkLeft.

uses MLABC;
begin
var students := DataFrame.FromCsvText('''
Имя,Класс
Анна,8А
Борис,8Б
Вера,8А
''');
var marks := DataFrame.FromCsvText('''
Имя,Балл
Анна,91
Борис,76
Глеб,68
''');
var res := students.Join(marks, 'Имя', jkLeft);
res.Print;
end.

В таком соединении все ученики из students останутся в результате. Если для ученика нет строки в marks, значения из правой таблицы будут пропущенными.

Иногда одной колонки для соединения недостаточно. Например, оценка задаётся не только именем ученика, но и предметом.

uses MLABC;
begin
var plan := DataFrame.FromCsvText('''
Имя,Предмет,Класс
Анна,Математика,8А
Анна,Информатика,8А
Борис,Математика,8Б
Борис,Информатика,8Б
''');
var marks := DataFrame.FromCsvText('''
Имя,Предмет,Балл
Анна,Математика,91
Анна,Информатика,96
Борис,Математика,76
''');
plan.Join(marks, ['Имя', 'Предмет'], jkLeft).Print;
end.

Здесь строка считается совпавшей только тогда, когда совпали оба ключа: Имя и Предмет.

Если ключевые столбцы называются по-разному, можно указать ключи левой и правой таблицы отдельно.

uses MLABC;
begin
var students := DataFrame.FromCsvText('''
Код,Имя
1,Анна
2,Борис
3,Вера
''');
var marks := DataFrame.FromCsvText('''
student_id,Балл
1,91
2,76
''');
students.Join(marks, ['Код'], ['student_id'], jkLeft).Print;
end.

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

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

uses MLABC;
begin
var sales := DataFrame.FromCsvText('''
Товар,Количество
Хлеб,2
Молоко,1
Яблоки,4
''');
var products := DataFrame.FromCsvText('''
Товар,Категория,Цена
Хлеб,Продукты,45.50
Молоко,Продукты,72.90
Яблоки,Продукты,58.25
''');
sales.Join(products, 'Товар')
.WithColumnFloat('Сумма', row -> row['Количество'] * row['Цена'])
.Print;
end.

Параметр JoinKind задаёт, какие строки попадут в результат:

  • jkInner — только совпавшие строки из обеих таблиц; используется по умолчанию.
  • jkLeft — все строки из левой таблицы и найденные данные из правой.
  • jkRight — все строки из правой таблицы и найденные данные из левой.
  • jkFull — все строки из обеих таблиц.
var inner := students.Join(marks, 'Имя');
var left := students.Join(marks, 'Имя', jkLeft);
var right := students.Join(marks, 'Имя', jkRight);
var full := students.Join(marks, 'Имя', jkFull);

Если в правой таблице есть столбец с тем же именем, что и в левой, и это не ключ, в результате он получает префикс right_.

  • Concat добавляет строки и требует одинаковую схему таблиц.
  • Join соединяет таблицы по ключевым столбцам.
  • Обычный Join без третьего параметра — это jkInner.
  • Для сохранения всех строк первой таблицы используйте jkLeft.
  • Ключи можно задавать одним именем, массивом имён или двумя массивами, если имена ключей в таблицах разные.
  • Исходные таблицы при объединении не изменяются.