Шаблон отчёта по ЛР №5 (R)

Unique_Reference

Author

ФИО студента: Кравец Дмитрий Вячеславович

Published

October 22, 2025

2.1

Загрузка библиотек

library(dplyr)

Присоединяю пакет: 'dplyr'
Следующие объекты скрыты от 'package:stats':

    filter, lag
Следующие объекты скрыты от 'package:base':

    intersect, setdiff, setequal, union
library(readr)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.2
✔ ggplot2   4.0.0     ✔ tibble    3.3.0
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.1.0     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

1. Чтение файла и создание копии для проверки

df_raw <- read_csv("lab5.csv")
Rows: 240 Columns: 3
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (1): org_name
dbl (2): org_id, revenue

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
df <- df_raw %>% select(-org_id)  # Убираем org_id для основной работы

2. Топ-10 самых частых org_name (после приведения к нижнему регистру)

df <- df %>% mutate(org_name_lower = str_to_lower(org_name))
top10 <- df %>% count(org_name_lower, sort = TRUE) %>% 
head(10)
print(top10)
# A tibble: 10 × 2
   org_name_lower          n
   <chr>               <int>
 1 газпром пао            12
 2 аэрофлот оао           11
 3 лукойл, россия         10
 4 пао сбербанк           10
 5 банк втб                9
 6 втб банк пао            9
 7 оао ржд                 9
 8 ростелеком (россия)     9
 9 газпром, ооо            8
10 яндекс                  8

2.2

3. Функция нормализации названий организаций

library(dplyr)
library(stringr)

normalize_org_name <- function(name) {
  name %>%
    str_to_lower() %>%
    str_replace_all("(bank|холдинг|россии|банк|ооо|пао|оао|ао|pjsc|llc|n\\.v\\.|россия|\\(.*?\\)|-|\\s+)", " ") %>%
    str_trim() %>%
    str_replace_all("\\s+", " ") %>%
    str_replace_all("[^а-яa-z0-9 ]", "") %>%
    sapply(function(x) {
      if (str_detect(x, "сбер|sber|публичное акционерное общество сбер")) {
        "сбер"
      } else if (str_detect(x, "ржд|rzd|российские железные дороги")) {
        "ржд"
      } else if (str_detect(x, "газпром|public joint stock company gazprom")) {
        "газпром"
      } else if (str_detect(x, "лукойл|lukoil")) {
        "лукойл"
      } else if (str_detect(x, "втб|vtb")) {
        "втб"
      } else if (str_detect(x, "aeroflot|аэрофлот российские авиалинии|аэрофлот")) {
        "аэрофлот"
      } else if (str_detect(x, "яндекс|lukoil|yandex")) {
        "яндекс"
      } else if (str_detect(x, "rostelecom|ростелеком")) {
        "ростелеком"
      } 
      else {
        x
      }
    }) %>%
    unlist()
}

# Применение
df <- df %>%
  mutate(canonical_key = normalize_org_name(org_name))

4. Какие варианты попали в ключ

variants_per_key <- df %>%
  group_by(canonical_key) %>%
  summarise(n_variants = n_distinct(org_name), .groups = "drop") %>%
  arrange(desc(n_variants)) %>% 
  head(5)
print(variants_per_key)
# A tibble: 5 × 2
  canonical_key n_variants
  <chr>              <int>
1 аэрофлот               5
2 втб                    5
3 газпром                5
4 лукойл                 5
5 ржд                    5

2.3

5. Для каждого canonical_key — эталонное название: самое частотное org_name

canon_names <- df %>%
  group_by(canonical_key, org_name) %>%
  summarise(n = n()) %>%
  arrange(canonical_key, desc(n)) %>%
  slice_max(n, n = 1, with_ties = FALSE) %>%
  select(canonical_key, canonical_name = org_name)
`summarise()` has grouped output by 'canonical_key'. You can override using the
`.groups` argument.

6. Reference-таблица: canonical_key → canonical_name

print(canon_names)
# A tibble: 8 × 2
# Groups:   canonical_key [8]
  canonical_key canonical_name     
  <chr>         <chr>              
1 аэрофлот      Аэрофлот ОАО       
2 втб           Банк ВТБ           
3 газпром       Газпром ПАО        
4 лукойл        Лукойл, Россия     
5 ржд           ОАО РЖД            
6 ростелеком    Ростелеком (Россия)
7 сбер          ПАО Сбербанк       
8 яндекс        Яндекс             

2.4

7. Замена всех org_name на canonical_name на основе reference-таблицы

df <- df %>%
  left_join(canon_names, by = "canonical_key") %>%
  mutate(org_name = canonical_name) %>%
  select(-canonical_name)

8. Количество уникальных организаций до/после нормализации

n_unique_before <- n_distinct(df_raw$org_name)
n_unique_after <- n_distinct(df$org_name)
cat("До нормализации:", n_unique_before, " После:", n_unique_after, "\n")
До нормализации: 40  После: 8 
cat("Сжато синонимов в один эталон:", n_unique_before - n_unique_after, "\n")
Сжато синонимов в один эталон: 32 

2.5

9. Качество кластеризации (возвращаем org_id)

df_full <- df %>%
  mutate(row_id = row_number()) %>%
  left_join(
    df_raw %>% 
    mutate(row_id = row_number()) %>%
    select(org_id, org_name, row_id),
    by = "row_id"
  )

Доля чистоты каждого кластера

purity <- df_full %>%
  group_by(canonical_key) %>%
  summarise(
    size = n(),
    max_freq = max(table(org_id)),
    purity = max_freq / size
  )

cat("Средняя чистота кластеров:", mean(purity$purity), "\n")
Средняя чистота кластеров: 1 
cat("Медианная чистота кластеров:", median(purity$purity), "\n")
Медианная чистота кластеров: 1 

Таблица ошибок: какие org_id чаще всего смешиваются

error_table <- df_full %>%
  group_by(canonical_key, org_id) %>%
  summarise(n = n()) %>%
  filter(n > 1) %>%
  arrange(desc(n))
`summarise()` has grouped output by 'canonical_key'. You can override using the
`.groups` argument.
print(error_table)
# A tibble: 8 × 3
# Groups:   canonical_key [8]
  canonical_key org_id     n
  <chr>          <dbl> <int>
1 аэрофлот           7    30
2 втб                8    30
3 газпром            2    30
4 лукойл             5    30
5 ржд                3    30
6 ростелеком         6    30
7 сбер               1    30
8 яндекс             4    30

2.6

10. Сумма revenue по эталонам vs по org_id

library(dplyr)

agg_canon <- df %>%
  group_by(org_name) %>%
  summarise(total_rev = sum(revenue, na.rm = TRUE)) %>%
  arrange(desc(total_rev))

agg_id <- df_raw %>%
  group_by(org_id) %>%
  summarise(total_rev = sum(revenue, na.rm = TRUE)) %>%
  arrange(desc(total_rev))

print(agg_canon)
# A tibble: 8 × 2
  org_name            total_rev
  <chr>                   <dbl>
1 ОАО РЖД                196220
2 Банк ВТБ               179770
3 Газпром ПАО            167070
4 ПАО Сбербанк           158640
5 Лукойл, Россия         150090
6 Яндекс                 148330
7 Аэрофлот ОАО           145100
8 Ростелеком (Россия)    142390
print(agg_id)
# A tibble: 8 × 2
  org_id total_rev
   <dbl>     <dbl>
1      3    196220
2      8    179770
3      2    167070
4      1    158640
5      5    150090
6      4    148330
7      7    145100
8      6    142390