# Читаем набор данных средствами "базового" R 
# Не забываем про параметры для парсинга названий столбцов
melanoma <- read.table("https://kodomo.fbb.msu.ru/FBB/year_23/R/immuno_response_2025.tsv", header = TRUE)
head(melanoma) # проверям, что датафрейм выглядит правдоподобно

# Подгружаем коллекцию пакетов tidyverse для более удобной работы с данными табличного вида
library(tidyverse)

# Пробуем читать тот же набор данных 
melanoma <- read_delim("https://kodomo.fbb.msu.ru/FBB/year_23/R/immuno_response_2025.tsv")
melanoma

# Пробуем читать тот же набор данных, но функцией, которая по умолчанию воспринимает табуляцию как разделители столбцов
# Обратите внимание на то, что "формат" файла, записанный в названии (например, "tsv") не гарантирует, что внутри используются табы в качестве разделителей
melanoma <- read_tsv("https://kodomo.fbb.msu.ru/FBB/year_23/R/immuno_response_2025.tsv")
melanoma

# Как посмотреть справку про функции чтения файлов (из пакета readr)
?read_delim

# Есть ли в наборе данных пропущенные значения?
is.na(melanoma) # Возвращает логический датафрейм той же размерности 

colSums(is.na(melanoma)) # Считает число NA в каждом столбце

sum(is.na(melanoma)) # Считает общее число NA в датафрейме

drop_na(melanoma) # Удаляет все строчки с хотя бы одним NA в любом столбце
# Обратите внимание на то, что в датафреймах бывают столбцы "важные" (например, для решения конкретной задачи) и "менее важные" (например, комментарии или второстепенные измерения)
# Удаление строк с NA в любых столбцах поэтому может быть неблагоразумно!


# Тот же код, но перезаписанный с использованием конвейера - пайпа
# Конвейер в базовом R - |> 
# Конвейер в tidyverse - %>%
# Оба оператора аналогичны друг другу, можете использовать любой из них
# Горячие клавиши - Ctrl/Cmd + Shift + M
melanoma |> is.na() |> colSums()

# Как взглянуть на несколько строк в датафрейме?
melanoma
head(melanoma, 5)
slice_head(melanoma, n = 5)
slice_sample(melanoma, n = 5) # Отобрать случайные

glimpse(melanoma)

# Создадим новый столбец (mean_PFS), в котором будет среднее значение, рассчитанное по столбцу PFS (progression free survival)
# Поскольку в столбце PFS присутствуют NA, необходимо их учесть
# Например, при вызове функции mean использовать параметр na.rm = TRUE, чтобы игнорировать NA при расчете среднего (иначе получится один результат - NA)
melanoma |> 
  mutate(mean_PFS = mean(PFS, na.rm = TRUE)) |> 
  glimpse()

# В качестве альтернативы можно перед созданием нового столбца удалить строчки с NA в столбце PFS
# Выбирайте подход, который наиболее оптимален в рамках решаемой вами задачи
melanoma |> 
  drop_na(PFS) |> 
  mutate(mean_PFS = mean(PFS)) |> 
  glimpse()

# Результат применения нескольких функций в конвейере можно записать в переменную с помощью <-
melanoma_flt <- melanoma |> 
  drop_na(PFS) |> 
  mutate(mean_PFS = mean(PFS)) |> 
  relocate(mean_PFS, .after = PFS) # переместить новый столбец mean_PFS после столбца PFS

# То же самое
# Направление стрелочки <- или -> показывает направление записи результата выполнения кода в переменную
melanoma |> 
  drop_na(PFS) |> 
  mutate(mean_PFS = mean(PFS)) |> 
  relocate(mean_PFS, .after = PFS) -> melanoma_flt

# Открыть датафрейм в окошке просмотровщика (или нажать на значок таблицы в панели Environment)
melanoma_flt |> view()


# Отобрать стобцы с идентификатором датасета или с названиями, начинающимися на P или T
melanoma |> 
  select(DatasetID, starts_with("P"), starts_with("T"))

# Tidyverse умеет работать с совершенно разными названиями столбцов
# Но лучше не усложнять себе жизнь и не использовать в названии столбцов (как и переменных) пробелы и спецсимволы (помимо точки и нижнего подчеркивания), не начинать их с цифр
# Здесь приведен пример плохого названия столбца
melanoma |> 
  # Создали столбец с комбинацией типа опухоли и варианта лечения
  mutate(`1 very BAD clmn` = paste(Cancer, Treatment, sep = "_&_")) |> 
  pull(`1 very BAD clmn`) |> 
  table() # Подсчитали количество таких комбинаций


# Другой подход для подсчета комбинаций - с помощью функции count (из tidyverse)
melanoma |> 
  mutate(`1 very BAD clmn` = paste(Cancer, Treatment, sep = "_&_")) |> 
  count(`1 very BAD clmn`, DatasetID) |> # можно считать по нескольким столбцам, возвращается датафрейм
  rename(counts = n) |> # переименовать столбец n (создан функцией count) в counts
  arrange(desc(counts)) |> # отсортировать по убыванию значений в столбце counts
  separate(`1 very BAD clmn`, into = c("Cancer", "Treatment"), sep = "_&_") # разделить строки в одном столбце на два столбца по указанному разделителю


# Разобъем значения PFS на две половины (lower и upper), а затем на квартили:
# от самых низких значений в Q1 до самых высоких в Q4 (вспомните боксплот)
melanoma |> 
  drop_na(PFS) |> 
  mutate(
    # Функция ntile сортирует значения PFS и приписывает номер группы, в которую попало значение (число групп указано: n = 4)
    quartile = ntile(PFS, n = 4),
    # Используем одно условие для того, чтобы пометить в нижнюю или верхнюю половину значений попало конкретное значение PFS
    quartile_label = if_else(quartile %in% c(1, 2), "lower", "upper"), # или ifelse()
    # Используем 3 условия (+ дефолтное, т.е. все остальное, что не удовлетворяет указанным условиям), чтобы перекодировать значения в столбце quartile в более подробные обозначения
    # case_when удобно использовать, когда есть много условий, не нужно писать много if_else подряд
    quartile_label_qs = case_when(
      quartile == 1 ~ "Q1 (lower)",
      quartile == 2 ~ "Q2",
      quartile == 3 ~ "Q3",
      .default = "Q4 (upper)")) |> 
  glimpse()
