#r
#r #r-faq
Вопрос:
У меня есть фрейм данных ( all_data
), в котором у меня есть список сайтов (от 1 … до n) и их оценки, например
site score
1 10
1 11
1 12
4 10
4 11
4 11
8 9
8 8
8 7
Я хочу создать столбец, который нумерует каждый уровень сайта в числовом порядке, например, счетчик. В примере сайты (1, 4 и 8) будут иметь соответствующий счетчик от 1 до 3 в столбце «номер»:
site score number
1 10 1
1 11 1
1 12 1
4 10 2
4 11 2
4 11 2
8 9 3
8 8 3
8 7 3
Я уверен, что это должно быть легко решено, но я еще не нашел способ.
Комментарии:
1. я полагаю, что это факторная конструкция в R.
Ответ №1:
Попробуйте Data$number <- as.numeric(as.factor(Data$site))
В качестве примечания: разница между решением me и @Chase, с одной стороны, и решением @DWin, с другой, заключается в порядке чисел. Оба as.factor
и factor
автоматически сортируют уровни, тогда как в решении @DWin этого не происходит :
Dat <- data.frame(site = rep(c(1,8,4), each = 3), score = runif(9))
Dat$number <- as.numeric(factor(Dat$site))
Dat$sitenum <- match(Dat$site, unique(Dat$site) )
Дает
> Dat
site score number sitenum
1 1 0.7377561 1 1
2 1 0.3131139 1 1
3 1 0.7862290 1 1
4 8 0.4480387 3 2
5 8 0.3873210 3 2
6 8 0.8778102 3 2
7 4 0.6916340 2 3
8 4 0.3033787 2 3
9 4 0.6552808 2 3
Комментарии:
1. при использовании as.factor() автоматически ли сортируются уровни?
2. @Brandon : Действительно, также при использовании
factor
. Решение, которое не выполняет сортировку, — это решение DWin . Добавлен пример к вопросу.3. Просто добавьте сюда примечание, в то время как оба метода (фактор и совпадение, уникальный) работают, последний масштабируется в зависимости от размера данных
Ответ №2:
Два других варианта:
1) С помощью .GRP
функции из data.table
пакета:
library(data.table)
setDT(dat)[, num := .GRP, by = site]
в примере набора данных, приведенном ниже, это приводит к:
> dat
site score num
1: 1 0.14945795 1
2: 1 0.60035697 1
3: 1 0.94643075 1
4: 8 0.68835336 2
5: 8 0.50553372 2
6: 8 0.37293624 2
7: 4 0.33580504 3
8: 4 0.04825135 3
9: 4 0.61894754 3
10: 8 0.96144729 2
11: 8 0.65496051 2
12: 8 0.51029199 2
2) Используя group_indices
функцию из dplyr
:
dat$num <- group_indices(dat, site)
или когда вы хотите обойти нестандартную оценку:
library(dplyr)
dat %>%
mutate(num = group_indices_(dat, .dots = c('site')))
что приводит к:
site score num
1 1 0.42480366 1
2 1 0.98736177 1
3 1 0.35766187 1
4 8 0.06243182 3
5 8 0.55617002 3
6 8 0.20304632 3
7 4 0.90855921 2
8 4 0.25215078 2
9 4 0.44981251 2
10 8 0.60288270 3
11 8 0.46946587 3
12 8 0.44941782 3
Как видно, dplyr
дает другой порядок номеров групп.
Если вам нужен другой номер при каждом изменении группы, есть несколько других вариантов:
1) с основанием R:
# option 1:
dat$num <- cumsum(c(TRUE, head(dat$site, -1) != tail(dat$site, -1)))
# option 2:
x <- rle(dat$site)$lengths
dat$num <- rep(seq_along(x), times=x)
2) с data.table
помощью пакета:
library(data.table)
setDT(dat)[, num := rleid(site)]
все это приводит к:
> dat
site score num
1 1 0.80817855 1
2 1 0.07881334 1
3 1 0.60092828 1
4 8 0.71477988 2
5 8 0.51384565 2
6 8 0.72011650 2
7 4 0.74994627 3
8 4 0.09564052 3
9 4 0.39782587 3
10 8 0.29446540 4
11 8 0.61725367 4
12 8 0.97427413 4
Используемые данные:
dat <- data.frame(site = rep(c(1,8,4,8), each = 3), score = runif(12))
Комментарии:
1. group_indices_() устарел. Какие-либо последствия?
Ответ №3:
В новом dplyr
1.0.0 мы можем использовать cur_group_id()
, который присваивает группе уникальный числовой идентификатор.
library(dplyr)
df %>% group_by(site) %>% mutate(number = cur_group_id())
# site score number
# <int> <int> <int>
#1 1 10 1
#2 1 11 1
#3 1 12 1
#4 4 10 2
#5 4 11 2
#6 4 11 2
#7 8 9 3
#8 8 8 3
#9 8 7 3
данные
df <- structure(list(site = c(1L, 1L, 1L, 4L, 4L, 4L, 8L, 8L, 8L),
score = c(10L, 11L, 12L, 10L, 11L, 11L, 9L, 8L, 7L)),
class = "data.frame", row.names = c(NA, -9L))
Ответ №4:
Это должно быть достаточно эффективным и понятным:
Dat$sitenum <- match(Dat$site, unique(Dat$site))
Ответ №5:
Используя данные из @Jaap, другая dplyr
возможность использования dense_rank()
может быть:
dat %>%
mutate(ID = dense_rank(site))
site score ID
1 1 0.1884490 1
2 1 0.1087422 1
3 1 0.7438149 1
4 8 0.1150771 3
5 8 0.9978203 3
6 8 0.7781222 3
7 4 0.4081830 2
8 4 0.2782333 2
9 4 0.9566959 2
10 8 0.2545320 3
11 8 0.1201062 3
12 8 0.5449901 3
Или rleid()
аналогичный dplyr
подход, при котором сначала упорядочиваются данные:
dat %>%
arrange(site) %>%
mutate(ID = with(rle(site), rep(seq_along(lengths), lengths)))
site score ID
1 1 0.1884490 1
2 1 0.1087422 1
3 1 0.7438149 1
4 4 0.4081830 2
5 4 0.2782333 2
6 4 0.9566959 2
7 8 0.1150771 3
8 8 0.9978203 3
9 8 0.7781222 3
10 8 0.2545320 3
11 8 0.1201062 3
12 8 0.5449901 3
Или с помощью duplicated()
и cumsum()
:
df %>%
mutate(ID = cumsum(!duplicated(site)))
То же самое с base R
:
df$ID <- with(rle(df$site), rep(seq_along(lengths), lengths))
Или:
df$ID <- cumsum(!duplicated(df$site))
Ответ №6:
Вы можете превратить site в фактор, а затем вернуть числовые или целочисленные значения этого фактора:
dat <- data.frame(site = rep(c(1,4,8), each = 3), score = runif(9))
dat$number <- as.integer(factor(dat$site))
dat
site score number
1 1 0.5305773 1
2 1 0.9367732 1
3 1 0.1831554 1
4 4 0.4068128 2
5 4 0.3438962 2
6 4 0.8123883 2
7 8 0.9122846 3
8 8 0.2949260 3
9 8 0.6771526 3
Ответ №7:
Другое решение с использованием data.table
пакета.
Пример с более полным набором данных, предоставленным Jaap:
setDT(dat)[, number := frank(site, ties.method = "dense")]
dat
site score number
1: 1 0.3107920 1
2: 1 0.3640102 1
3: 1 0.1715318 1
4: 8 0.7247535 3
5: 8 0.1263025 3
6: 8 0.4657868 3
7: 4 0.6915818 2
8: 4 0.3558270 2
9: 4 0.3376173 2
10: 8 0.7934963 3
11: 8 0.9641918 3
12: 8 0.9832120 3
Ответ №8:
Другой способ сделать это. Я думаю, это легко получить, даже если вы мало знаете о R:
library(dplyr)
df <- data.frame('site' = c(1, 1, 1, 4, 4, 4, 8, 8, 8))
df <- mutate(df, 'number' = cumsum(site != lag(site, default=-1)))
Ответ №9:
Если вы хотите сохранить существующие столбцы и назначить обратно в тот же фрейм данных…
my_df <- my_df %>%
select(everything()) %>%
group_by(geo) %>%
mutate(geo_id = cur_group_id())
И вы можете сделать несколько столбцов таким образом…
my_df <- my_df %>%
select(everything()) %>%
group_by(geo) %>%
mutate(geo_id = cur_group_id()) %>%
group_by(state) %>%
mutate(state_id = cur_group_id()) %>%
group_by(name) %>%
mutate(name_id = cur_group_id())
Ответ №10:
Мне тоже недавно понадобилось решение этой проблемы. Не нашел эту тему, запустил мою и был перенаправлен сюда (спасибо). Приятно видеть много решений, но для меня (и я считаю, что это хорошая практика) важно масштабируемое решение. Следовательно, мы провели сравнительный анализ нескольких решений, приведенных ниже.
df <- data.table(country = rep(c('a', 'b', 'b', 'c', 'c', 'c'), 1e7)
)
a <-
microbenchmark(factor = {df[, group_id := as.integer(factor(country))]}
, unique_match = df[, group_id := match(country, unique(country))]
, rle = df[ , group_id := with(rle(country), rep(seq_along(lengths), lengths))]
, dup_cumsum = df[, group_id := cumsum(!duplicated(country))]
, frank = df[, group_id := frank(country, ties.method = "dense")]
, GRP = df[, group_id := .GRP, country]
, rleid = df[, group_id := rleid(country)]
, cumsum_head_tail = df[, group_id := cumsum(c(TRUE, head(country, -1) != tail(country, -1)))]
, times = 50
)
autoplot(a)
Казалось бы, подиум удерживается data.table
.
Тем не менее, было здорово узнать об альтернативах, например cumsum(!duplicated(country))
. Какая головоломка!
Комментарии:
1. Пожалуйста, обратите внимание, что результат может отличаться между решениями на основе «длины выполнения» и другими. Например.
x = c(2, 2, 4, 4, 2)
;data.table::rleid(x)
;as.numeric(as.factor(x))
2. @Henrik вы правы. Я заглянул в
rleid
справку. Похоже, это не параметр для случаев, когда вектор не упорядочен. Правильно ли я говорюrleid
, что вектор должен быть отсортирован заранее?3. Ну, суть функций длины выполнения заключается в отслеживании последовательных запусков. Например. если вы хотите провести различие между двумя запусками по 2 в моем крошечном примере, очевидно, что вам не следует сначала сортировать вектор. Выбор функции зависит от вашей цели.
Ответ №11:
Если номера site
столбца были неупорядоченными, мы могли бы использовать as_factor()
в сочетании с fct_inorder()
из forcats
пакета:
library(tibble)
library(dplyr)
library(forcats)
all_data_unordered <- tibble(site = c(1,1,1,8,8,8,4,4,4),
score = c(10,11,12,10,11,11,9,8,7))
all_data_unordered |>
mutate(number = as_factor(site) |> fct_inorder() |> as.integer())
#> # A tibble: 9 × 3
#> site score number
#> <dbl> <dbl> <int>
#> 1 1 10 1
#> 2 1 11 1
#> 3 1 12 1
#> 4 8 10 2
#> 5 8 11 2
#> 6 8 11 2
#> 7 4 9 3
#> 8 4 8 3
#> 9 4 7 3
Создано 2021-11-05 пакетом reprex (v2.0.1)