Как изменить нумерацию списка при изменении значения - PullRequest
9 голосов
/ 18 октября 2011

У меня есть 2 списка чисел (col1 и col2) ниже.Я хотел бы добавить 2 столбца (col3 и col4), которые делают следующее.номера col3 col2 начинаются с 1 каждый раз, когда изменяется col2 (например, с b2 на b3).col4 имеет значение TRUE для последнего вхождения для каждого значения в col2.

Данные сортируются по col1, затем col2 для начала.Заметка.Значения в col2 могут встречаться для разных значений col1.(т.е. у меня может быть b1 для каждого значения col 1 (a, b, c))

Я могу получить этот рабочий штраф для ~ 5000 строк (~ 6 секунд), но масштабируя его до ~ 1 миллиона строк,вешает трубку.

Вот мой код

df$col3 <- 0
df$col4 <- FALSE
stopHere <- nrow(df)
c1 <- 'xxx'
c2 <- 'xxx'
for (i in 1:stopHere) {
  if (df[i, "col1"] != c1) {
    c2 <- 0
    c3 <- 1
    c1 <- df[i, "col1"]
  }
  if (df[i, "col2"] != c2) {
    df[i - 1, "col4"] <- TRUE
    c3 <- 1
    c2  <- df[i, "col2"]
  }
  df[i, "col3"] <- c3
  c3  <- c3 + 1
}

Это мой желаемый вывод.

1     a   b1    1 FALSE
2     a   b1    2 FALSE
3     a   b1    3  TRUE
4     a   b2    1 FALSE
5     a   b2    2  TRUE
6     a   b3    1 FALSE
7     a   b3    2 FALSE
8     a   b3    3 FALSE
9     a   b3    4 FALSE
10    a   b3    5  TRUE
11    b   b1    1 FALSE
12    b   b1    2 FALSE
13    b   b1    3 FALSE
14    b   b1    4  TRUE
15    b   b2    1 FALSE
16    b   b2    2 FALSE
17    b   b2    3 FALSE
18    b   b2    4  TRUE
19    c   b1    1  TRUE
20    c   b2    1 FALSE
21    c   b2    2 FALSE
22    c   b2    3  TRUE
23    c   b3    1 FALSE
24    c   b3    2  TRUE
25    c   b4    1 FALSE
26    c   b4    2 FALSE
27    c   b4    3 FALSE
28    c   b4    4 FALSE

Ответы [ 4 ]

9 голосов
/ 19 октября 2011

Вот векторизованное решение, которое работает с вашими примерами данных:

dat <- data.frame(
  V1 = rep(letters[1:3], c(10, 8, 10)),
  V2 = rep(paste("b", c(1:3, 1:2, 1:4) ,sep=""), c(3, 2, 5, 4, 4, 1, 3, 2, 4))
  )

Создание столбцов 3 и 4

zz <- rle(as.character(dat$V2))$lengths
dat$V3 <- sequence(zz)
dat$V4 <- FALSE
dat$V4[head(cumsum(zz), -1)] <- TRUE

Результаты:

dat
   V1 V2 V3    V4
1   a b1  1 FALSE
2   a b1  2 FALSE
3   a b1  3  TRUE
4   a b2  1 FALSE
5   a b2  2  TRUE
6   a b3  1 FALSE
7   a b3  2 FALSE
8   a b3  3 FALSE
9   a b3  4 FALSE
10  a b3  5  TRUE
11  b b1  1 FALSE
12  b b1  2 FALSE
13  b b1  3 FALSE
14  b b1  4  TRUE
15  b b2  1 FALSE
16  b b2  2 FALSE
17  b b2  3 FALSE
18  b b2  4  TRUE
19  c b1  1  TRUE
20  c b2  1 FALSE
21  c b2  2 FALSE
22  c b2  3  TRUE
23  c b3  1 FALSE
24  c b3  2  TRUE
25  c b4  1 FALSE
26  c b4  2 FALSE
27  c b4  3 FALSE
28  c b4  4 FALSE
6 голосов
/ 19 октября 2011

Некоторые примеры данных были бы полезны. Тем не менее, это должно быть хорошим местом для начала. С 3 уникальными значениями в col1 и 4 в col2, это займет всего секунду для 10 ^ 6 строк:

n = 10^6

col1 = sample(c('a', 'b', 'c'), n, replace=T)
col2 = sample(paste('b', 1:4, sep=''), n, replace=T)

data = data.frame(col1, col2, col3=0, col4=FALSE)
data = data[do.call(order, data), ]

data$col3 = unlist(t(tapply(as.numeric(data$col2), data[,1:2], function(x) 1:length(x))))
data$col4[c(diff(data$col3), -1) < 0] = TRUE
3 голосов
/ 19 октября 2011

Во-первых, сделайте ваши исходные данные воспроизводимыми и сделайте столбцы col1 и col2 в кадре данных.

dat <- read.table(textConnection(
"a   b1
a   b1
a   b1
a   b2
a   b2
a   b3
a   b3
a   b3
a   b3
a   b3
b   b1
b   b1
b   b1
b   b1
b   b2
b   b2
b   b2
b   b2
c   b1
c   b2
c   b2
c   b2
c   b3
c   b3
c   b4
c   b4
c   b4
c   b4"), stringsAsFactors=FALSE)
names(dat) <- c("col1", "col2")

Кодирование длин серий дает длины ваших последовательностей, поскольку все начинается с сортировки.

runs <- rle(dat$col2)

Теперь управляйте этой информацией.Для каждого элемента в компоненте длины создайте последовательность этой длины и соедините их все вместе.Признаки значений TRUE для col4 можно получить из cumsum длин.

dat$col3 <- unlist(sapply(runs$lengths, function(l) seq(length.out=l)))
dat$col4 <- FALSE
dat$col4[cumsum(runs$lengths)] <- TRUE

Для результата:

> dat
   col1 col2 col3  col4
1     a   b1    1 FALSE
2     a   b1    2 FALSE
3     a   b1    3  TRUE
4     a   b2    1 FALSE
5     a   b2    2  TRUE
6     a   b3    1 FALSE
7     a   b3    2 FALSE
8     a   b3    3 FALSE
9     a   b3    4 FALSE
10    a   b3    5  TRUE
11    b   b1    1 FALSE
12    b   b1    2 FALSE
13    b   b1    3 FALSE
14    b   b1    4  TRUE
15    b   b2    1 FALSE
16    b   b2    2 FALSE
17    b   b2    3 FALSE
18    b   b2    4  TRUE
19    c   b1    1  TRUE
20    c   b2    1 FALSE
21    c   b2    2 FALSE
22    c   b2    3  TRUE
23    c   b3    1 FALSE
24    c   b3    2  TRUE
25    c   b4    1 FALSE
26    c   b4    2 FALSE
27    c   b4    3 FALSE
28    c   b4    4  TRUE

Обратите внимание, что последнийстрока имеет col4 TRUE, которая соответствует вашему письменному описанию (последний из набора TRUE), но не соответствует вашему примеру вывода.Я не знаю, что вы хотите.

1 голос
/ 19 октября 2011

Это решение не требует ни циклов, ни rle, ни других умных функций; просто функции merge и aggregate.

Сначала готовятся ваши данные (использовался код Андри):

df <- data.frame(
  x = rep(letters[1:3], c(10, 8, 10)),
  y = rep(paste("b", c(1:3, 1:2, 1:4) ,sep=""), c(3, 2, 5, 4, 4, 1, 3, 2, 4))
)

Решение:

minmax <- with(df, merge(
                aggregate(seq(x), by = list(x = x, y = y), min),
                aggregate(seq(x), by = list(x = x, y = y), max)
          ))

names(minmax)[3:4] = c("min", "max") # unique pairs with min/max global order

result <- with(merge(df, minmax), 
    data.frame(x, y, count = seq(x) - min + 1, last = seq(x) == max))

В этом решении предполагается, что входные данные отсортированы, как вы сказали, но их можно легко изменить для работы с несортированными таблицами (и сохранить их в несортированном виде). ​​

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...