Методы анализа больших данных (Syllabus) 2025/Lesson ML: различия между версиями
Материал из Поле цифровой дидактики
Patarakin (обсуждение | вклад) |
Patarakin (обсуждение | вклад) |
||
| (не показано 8 промежуточных версий этого же участника) | |||
| Строка 1: | Строка 1: | ||
; Урок по анализу данных с использованием методов машинного обучения | |||
=== Диаграмма 1 === | |||
{{# | <uml> | ||
@startuml | |||
participant "R скрипт" as R | |||
participant "HTTP клиент" as HTTP | |||
participant "Википедия API" as API | |||
database "БД Википедии" as DB | |||
R -> HTTP: Формирует запрос | |||
HTTP -> API: Отправляет GET/POST запрос | |||
API -> DB: Запрашивает данные | |||
DB -> API: Возвращает данные | |||
API -> HTTP: JSON ответ | |||
HTTP -> R: Парсит JSON | |||
@enduml | |||
</uml> | |||
== Диаграмма 2 == | |||
<uml> | |||
@startuml | |||
start | |||
:Получить список категорий; | |||
:Для каждой статьи; | |||
:Запросить текст через API; | |||
:Сохранить в переменную; | |||
:Проверить качество текста; | |||
if (Текст валиден?) then (да) | |||
:Добавить в корпус; | |||
else (нет) | |||
:Пропустить; | |||
endif | |||
:Следующая статья; | |||
stop | |||
@enduml | |||
</uml> | |||
=== Диаграмма процесса очистки текста === | |||
<uml> | |||
@startuml | |||
partition "Очистка текста" { | |||
:Исходный текст; | |||
:Приведение к нижнему регистру; | |||
:Удаление URL и спец. символов; | |||
:Токенизация; | |||
:Удаление стоп-слов; | |||
:Стемминг; | |||
:Очищенные токены; | |||
} | |||
@enduml | |||
</uml> | |||
== Процесс получения данных == | |||
<syntaxhighlight lang="R" line> | |||
library(httr) | |||
library(jsonlite) | |||
library(xml2) | |||
library(tidyverse) | |||
library(quanteda) | |||
# Установка дополнительных пакетов | |||
library(stopwords) | |||
library(stringr) | |||
########### | |||
get_wikipedia_category_members <- function(category_name, language = "ru") { | |||
# Базовый URL API | |||
base_url <- paste0("https://", language, ".wikipedia.org/w/api.php") | |||
all_members <- data.frame() | |||
continue_token <- NULL | |||
repeat { | |||
# Формируем параметры запроса | |||
params <- list( | |||
action = "query", | |||
list = "categorymembers", | |||
cmtitle = paste0("Category:", category_name), | |||
cmlimit = 500, # Максимум результатов за раз | |||
format = "json", | |||
cmtype = "page", # Только статьи, не категории | |||
cmcontinue = continue_token | |||
) | |||
# Выполняем запрос | |||
response <- GET(base_url, query = params) | |||
# Проверяем статус ответа | |||
if (status_code(response) != 200) { | |||
warning("Ошибка при запросе к API: статус ", status_code(response)) | |||
break | |||
} | |||
# Парсим JSON | |||
data <- fromJSON(content(response, as = "text", encoding = "UTF-8")) | |||
# Извлекаем члены категории | |||
if (!is.null(data$query$categorymembers)) { | |||
members <- data$query$categorymembers %>% | |||
as_tibble() %>% | |||
select(pageid, title) | |||
all_members <- bind_rows(all_members, members) | |||
} | |||
# Проверяем, есть ли продолжение запроса | |||
if (is.null(data$`query-continue`$categorymembers$cmcontinue)) { | |||
break | |||
} | |||
continue_token <- data$`query-continue`$categorymembers$cmcontinue | |||
} | |||
return(all_members) | |||
} | |||
iot_articles <- get_wikipedia_category_members("Интернет вещей") | |||
cat("Найдено статей:", nrow(iot_articles), "\n") | |||
head(iot_articles) | |||
###################### | |||
get_wikipedia_text <- function(article_title, language = "ru") { | |||
base_url <- paste0("https://", language, ".wikipedia.org/w/api.php") | |||
params <- list( | |||
action = "query", | |||
titles = article_title, | |||
prop = "extracts", | |||
explaintext = TRUE, # Получаем чистый текст без Wiki-разметки | |||
format = "json" | |||
) | |||
response <- GET(base_url, query = params) | |||
if (status_code(response) != 200) { | |||
return(NA) | |||
} | |||
data <- fromJSON(content(response, as = "text", encoding = "UTF-8")) | |||
# Извлекаем текст из ответа | |||
pages <- data$query$pages | |||
page_id <- names(pages)[1] | |||
if (!is.null(pages[[page_id]]$extract)) { | |||
return(pages[[page_id]]$extract) | |||
} else { | |||
return(NA) | |||
} | |||
} | |||
# Сбираем тексты для всех найденных статей | |||
# Добавляем задержку между запросами, чтобы не перегружать сервер | |||
collected_texts <- tibble( | |||
title = character(), | |||
text = character() | |||
) | |||
for (i in seq_len(min(nrow(iot_articles), 50))) { # Возьмём первые 50 статей для примера | |||
if (i %% 10 == 0) { | |||
cat("Обработано статей:", i, "\n") | |||
} | |||
article_title <- iot_articles$title[i] | |||
article_text <- get_wikipedia_text(article_title) | |||
if (!is.na(article_text)) { | |||
collected_texts <- add_row(collected_texts, | |||
title = article_title, | |||
text = article_text) | |||
} | |||
# Задержка между запросами (500 мс) | |||
Sys.sleep(0.5) | |||
} | |||
cat("Успешно получено текстов:", nrow(collected_texts), "\n") | |||
########################### | |||
# Функция для предварительной очистки текста | |||
preprocess_text <- function(text) { | |||
# Приведение к нижнему регистру | |||
text <- tolower(text) | |||
# Удаление URL | |||
text <- str_remove_all(text, "https?://[^\\s]+") | |||
# Удаление специальных символов, но сохраняем буквы и пробелы | |||
text <- str_remove_all(text, "[^а-яА-Яa-zA-Z\\s]") | |||
# Удаление множественных пробелов | |||
text <- str_squish(text) | |||
return(text) | |||
} | |||
# Применяем предварительную очистку | |||
cleaned_texts <- collected_texts %>% | |||
mutate( | |||
cleaned_text = map_chr(text, preprocess_text) | |||
) | |||
head(cleaned_texts$cleaned_text, 1) | |||
### Работа с пакетом quanteda | |||
# Создание корпуса | |||
corpus_texts <- corpus(cleaned_texts, | |||
docid_field = "title", | |||
text_field = "cleaned_text") | |||
cat("Корпус создан. Документов:", ndoc(corpus_texts), "\n") | |||
cat("Токенов:", ntoken(corpus_texts), "\n") | |||
# Токенизация текста | |||
tokens_data <- tokens(corpus_texts, | |||
remove_punct = TRUE, | |||
remove_numbers = TRUE, | |||
remove_separators = TRUE) | |||
# Приведение к нижнему регистру | |||
tokens_data <- tokens_tolower(tokens_data) | |||
# Получение русских стоп-слов | |||
russian_stopwords <- stopwords("russian") | |||
# Удаление стоп-слов | |||
tokens_clean <- tokens_select(tokens_data, | |||
pattern = russian_stopwords, | |||
selection = "remove", | |||
min_nchar = 3) # Удаляем слова короче 3 символов | |||
# Стемминг (приведение к корню слова) | |||
tokens_stemmed <- tokens_wordstem(tokens_clean, language = "russian") | |||
cat("Размер словаря после очистки:", ntype(tokens_stemmed), "\n") | |||
# Создание матрицы документ-термин | |||
dtm <- dfm(tokens_stemmed) | |||
cat("Размеры DTM: ", nrow(dtm), " документов, ", ncol(dtm), " термов\n", sep = "") | |||
# Просмотр структуры | |||
head(dtm) | |||
# Топ-10 самых частых слов | |||
topfeatures(dtm, 10) | |||
# Фильтрация редких слов | |||
# Оставляем слова, которые встречаются минимум в 2 документах | |||
# и имеют минимум 5 вхождений в корпусе | |||
dtm_trimmed <- dfm_trim(dtm, | |||
min_docfreq = 2, # мин. документы | |||
min_termfreq = 5) # мин. вхождения | |||
cat("Размеры отфильтрованной DTM: ", nrow(dtm_trimmed), " документов, ", | |||
ncol(dtm_trimmed), " термов\n", sep = "") | |||
# Вычисление TF-IDF (Term Frequency-Inverse Document Frequency) | |||
dtm_tfidf <- dfm_tfidf(dtm_trimmed) | |||
cat("TF-IDF матрица готова\n") | |||
### Статистика текстов | |||
# Получение статистики корпуса | |||
corpus_stats <- tokens_data %>% | |||
ntoken() %>% | |||
tibble(doc_id = names(.), n_tokens = .) | |||
# Визуализация статистики | |||
library(ggplot2) | |||
ggplot(corpus_stats, aes(x = n_tokens)) + | |||
geom_histogram(bins = 20, fill = "steelblue", color = "black") + | |||
labs(title = "Распределение количества токенов в документах", | |||
x = "Количество токенов", | |||
y = "Количество документов") + | |||
theme_minimal() | |||
# Средняя длина документа | |||
cat("Средняя длина документа:", mean(corpus_stats$n_tokens), "токенов\n") | |||
</syntaxhighlight> | |||
Текущая версия от 12:35, 14 ноября 2025
- Урок по анализу данных с использованием методов машинного обучения
Диаграмма 1

Диаграмма 2

Диаграмма процесса очистки текста

Процесс получения данных
library(httr)
library(jsonlite)
library(xml2)
library(tidyverse)
library(quanteda)
# Установка дополнительных пакетов
library(stopwords)
library(stringr)
###########
get_wikipedia_category_members <- function(category_name, language = "ru") {
# Базовый URL API
base_url <- paste0("https://", language, ".wikipedia.org/w/api.php")
all_members <- data.frame()
continue_token <- NULL
repeat {
# Формируем параметры запроса
params <- list(
action = "query",
list = "categorymembers",
cmtitle = paste0("Category:", category_name),
cmlimit = 500, # Максимум результатов за раз
format = "json",
cmtype = "page", # Только статьи, не категории
cmcontinue = continue_token
)
# Выполняем запрос
response <- GET(base_url, query = params)
# Проверяем статус ответа
if (status_code(response) != 200) {
warning("Ошибка при запросе к API: статус ", status_code(response))
break
}
# Парсим JSON
data <- fromJSON(content(response, as = "text", encoding = "UTF-8"))
# Извлекаем члены категории
if (!is.null(data$query$categorymembers)) {
members <- data$query$categorymembers %>%
as_tibble() %>%
select(pageid, title)
all_members <- bind_rows(all_members, members)
}
# Проверяем, есть ли продолжение запроса
if (is.null(data$`query-continue`$categorymembers$cmcontinue)) {
break
}
continue_token <- data$`query-continue`$categorymembers$cmcontinue
}
return(all_members)
}
iot_articles <- get_wikipedia_category_members("Интернет вещей")
cat("Найдено статей:", nrow(iot_articles), "\n")
head(iot_articles)
######################
get_wikipedia_text <- function(article_title, language = "ru") {
base_url <- paste0("https://", language, ".wikipedia.org/w/api.php")
params <- list(
action = "query",
titles = article_title,
prop = "extracts",
explaintext = TRUE, # Получаем чистый текст без Wiki-разметки
format = "json"
)
response <- GET(base_url, query = params)
if (status_code(response) != 200) {
return(NA)
}
data <- fromJSON(content(response, as = "text", encoding = "UTF-8"))
# Извлекаем текст из ответа
pages <- data$query$pages
page_id <- names(pages)[1]
if (!is.null(pages[[page_id]]$extract)) {
return(pages[[page_id]]$extract)
} else {
return(NA)
}
}
# Сбираем тексты для всех найденных статей
# Добавляем задержку между запросами, чтобы не перегружать сервер
collected_texts <- tibble(
title = character(),
text = character()
)
for (i in seq_len(min(nrow(iot_articles), 50))) { # Возьмём первые 50 статей для примера
if (i %% 10 == 0) {
cat("Обработано статей:", i, "\n")
}
article_title <- iot_articles$title[i]
article_text <- get_wikipedia_text(article_title)
if (!is.na(article_text)) {
collected_texts <- add_row(collected_texts,
title = article_title,
text = article_text)
}
# Задержка между запросами (500 мс)
Sys.sleep(0.5)
}
cat("Успешно получено текстов:", nrow(collected_texts), "\n")
###########################
# Функция для предварительной очистки текста
preprocess_text <- function(text) {
# Приведение к нижнему регистру
text <- tolower(text)
# Удаление URL
text <- str_remove_all(text, "https?://[^\\s]+")
# Удаление специальных символов, но сохраняем буквы и пробелы
text <- str_remove_all(text, "[^а-яА-Яa-zA-Z\\s]")
# Удаление множественных пробелов
text <- str_squish(text)
return(text)
}
# Применяем предварительную очистку
cleaned_texts <- collected_texts %>%
mutate(
cleaned_text = map_chr(text, preprocess_text)
)
head(cleaned_texts$cleaned_text, 1)
### Работа с пакетом quanteda
# Создание корпуса
corpus_texts <- corpus(cleaned_texts,
docid_field = "title",
text_field = "cleaned_text")
cat("Корпус создан. Документов:", ndoc(corpus_texts), "\n")
cat("Токенов:", ntoken(corpus_texts), "\n")
# Токенизация текста
tokens_data <- tokens(corpus_texts,
remove_punct = TRUE,
remove_numbers = TRUE,
remove_separators = TRUE)
# Приведение к нижнему регистру
tokens_data <- tokens_tolower(tokens_data)
# Получение русских стоп-слов
russian_stopwords <- stopwords("russian")
# Удаление стоп-слов
tokens_clean <- tokens_select(tokens_data,
pattern = russian_stopwords,
selection = "remove",
min_nchar = 3) # Удаляем слова короче 3 символов
# Стемминг (приведение к корню слова)
tokens_stemmed <- tokens_wordstem(tokens_clean, language = "russian")
cat("Размер словаря после очистки:", ntype(tokens_stemmed), "\n")
# Создание матрицы документ-термин
dtm <- dfm(tokens_stemmed)
cat("Размеры DTM: ", nrow(dtm), " документов, ", ncol(dtm), " термов\n", sep = "")
# Просмотр структуры
head(dtm)
# Топ-10 самых частых слов
topfeatures(dtm, 10)
# Фильтрация редких слов
# Оставляем слова, которые встречаются минимум в 2 документах
# и имеют минимум 5 вхождений в корпусе
dtm_trimmed <- dfm_trim(dtm,
min_docfreq = 2, # мин. документы
min_termfreq = 5) # мин. вхождения
cat("Размеры отфильтрованной DTM: ", nrow(dtm_trimmed), " документов, ",
ncol(dtm_trimmed), " термов\n", sep = "")
# Вычисление TF-IDF (Term Frequency-Inverse Document Frequency)
dtm_tfidf <- dfm_tfidf(dtm_trimmed)
cat("TF-IDF матрица готова\n")
### Статистика текстов
# Получение статистики корпуса
corpus_stats <- tokens_data %>%
ntoken() %>%
tibble(doc_id = names(.), n_tokens = .)
# Визуализация статистики
library(ggplot2)
ggplot(corpus_stats, aes(x = n_tokens)) +
geom_histogram(bins = 20, fill = "steelblue", color = "black") +
labs(title = "Распределение количества токенов в документах",
x = "Количество токенов",
y = "Количество документов") +
theme_minimal()
# Средняя длина документа
cat("Средняя длина документа:", mean(corpus_stats$n_tokens), "токенов\n")
