Скопируйте значение в n предыдущих ячеек столбца в кадре данных на основе идентификатора и даты в Python / R

1

Я пытаюсь найти лучший способ заполнить столбец в DataFrame, основанный на значениях в комбинации оставшихся столбцов.

Я хочу создать столбец v2, так что каждый раз, когда 1 встречается в v1, предыдущие 3 дат и дата, когда 1 столкнулась, для одного и того же ID, должны быть заполнены 1s в новом столбце.

Это то, что DataFrame будет выглядеть первоначально:

        date    id  v1
0  2017-05-29  5206   0
1  2017-05-30  5206   0
2  2017-05-31  5206   0
3  2017-06-01  5206   0
4  2017-06-02  5206   0
5  2017-06-03  5206   0
6  2017-06-04  5206   1
7  2017-05-29  5207   0
8  2017-05-30  5207   1
9  2017-05-31  5207   0
10 2017-06-01  5207   1
11 2017-06-02  5207   0
12 2017-06-03  5207   0
13 2017-06-04  5207   0
14 2017-05-29  5208   0
15 2017-05-30  5208   1
16 2017-05-31  5208   0
17 2017-06-01  5208   0
18 2017-06-02  5208   0
19 2017-06-03  5208   0
20 2017-06-04  5208   1

И я хочу:

         date    id  v1  v2
0  2017-05-29  5206   0   0
1  2017-05-30  5206   0   0
2  2017-05-31  5206   0   0
3  2017-06-01  5206   0   1
4  2017-06-02  5206   0   1
5  2017-06-03  5206   0   1
6  2017-06-04  5206   1   1
7  2017-05-29  5207   0   1
8  2017-05-30  5207   1   1
9  2017-05-31  5207   0   1
10 2017-06-01  5207   1   1
11 2017-06-02  5207   0   0
12 2017-06-03  5207   0   0
13 2017-06-04  5207   0   0
14 2017-05-29  5208   0   1
15 2017-05-30  5208   1   1
16 2017-05-31  5208   0   0
17 2017-06-01  5208   0   1
18 2017-06-02  5208   0   1
19 2017-06-03  5208   0   1
20 2017-06-04  5208   1   1

Код для воссоздания исходного образца DataFrame в Python приведен ниже:

import pandas as pd
import datetime
base = datetime.datetime.today()-datetime.timedelta(days=500)

df=pd.DataFrame()
df['date']=3*[base + datetime.timedelta(days=x) for x in range(0, 7)]
df['date']=df['date'].dt.floor('d')
df['id']=sorted(7*list(range(5206,5209)))
df['v1']=[0,0,0,0,0,0,1,
  0,1,0,1,0,0,0,
  0,1,0,0,0,0,1]

Также обратите внимание, что я не возражаю, если это делается с использованием R, так как у меня нет предпочтения между R и Python для этой задачи.

Теги:
pandas
dataframe

2 ответа

2
Лучший ответ

С R и data.table:

library(data.table)
setDT(DF)

DF[, v := do.call(pmax, shift(v1, 0:3, type="lead", fill=0L)), by=id]

          date   id v1 v2 v
 1: 2017-05-29 5206  0  0 0
 2: 2017-05-30 5206  0  0 0
 3: 2017-05-31 5206  0  0 0
 4: 2017-06-01 5206  0  1 1
 5: 2017-06-02 5206  0  1 1
 6: 2017-06-03 5206  0  1 1
 7: 2017-06-04 5206  1  1 1
 8: 2017-05-29 5207  0  1 1
 9: 2017-05-30 5207  1  1 1
10: 2017-05-31 5207  0  1 1
11: 2017-06-01 5207  1  1 1
12: 2017-06-02 5207  0  0 0
13: 2017-06-03 5207  0  0 0
14: 2017-06-04 5207  0  0 0
15: 2017-05-29 5208  0  1 1
16: 2017-05-30 5208  1  1 1
17: 2017-05-31 5208  0  0 0
18: 2017-06-01 5208  0  1 1
19: 2017-06-02 5208  0  1 1
20: 2017-06-03 5208  0  1 1
21: 2017-06-04 5208  1  1 1
          date   id v1 v2 v

Как это работает: shift с типом "свинец" смотрит вперед, в этом случае на расстояния 0, 1, 2 или 3 (с неопределенными значениями заменяется на ноль). pmax ищет по величине максимальное значение по этим векторам.


Аналогично, из комментария @RyanD:

DF[order(date), v := 
  do.call(pmax, shift(v1, 0:3, type="lead", fill=0L))
, by=id]

Это имеет то преимущество, что оно работает, даже если данные не сортируются по date. Он сортирует данные временно только при построении столбца.


В качестве альтернативы выполните скользящее соединение:

DF[, date := as.IDate(date)] # format
DF[, v := DF[v1 == 1][.SD, on=.(id, date), roll=-3, .N, by=.EACHI]$N]

Это имеет то преимущество, что оно работает, даже если перечисление дат является неполным. Он просматривает каждую строку DF в DF[v1 == 1], считая любое соответствие 0-3 дня в будущем.


Данные:

DF = structure(list(date = c("2017-05-29", "2017-05-30", "2017-05-31", 
"2017-06-01", "2017-06-02", "2017-06-03", "2017-06-04", "2017-05-29", 
"2017-05-30", "2017-05-31", "2017-06-01", "2017-06-02", "2017-06-03", 
"2017-06-04", "2017-05-29", "2017-05-30", "2017-05-31", "2017-06-01", 
"2017-06-02", "2017-06-03", "2017-06-04"), id = c(5206L, 5206L, 
5206L, 5206L, 5206L, 5206L, 5206L, 5207L, 5207L, 5207L, 5207L, 
5207L, 5207L, 5207L, 5208L, 5208L, 5208L, 5208L, 5208L, 5208L, 
5208L), v1 = c(0L, 0L, 0L, 0L, 0L, 0L, 1L, 0L, 1L, 0L, 1L, 0L, 
0L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 1L), v2 = c(0L, 0L, 0L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 1L, 1L, 0L, 1L, 1L, 1L, 1L
)), .Names = c("date", "id", "v1", "v2"), row.names = c(NA, -21L
), class = "data.frame")
  • 1
    Это прекрасно работает! Я буду использовать скользящее соединение, поскольку оно не требует предварительной сортировки столбцов в хронологическом порядке. Спасибо!
1

Решение от панд, с bfill и limit

df.v1.where(df.v1==1).groupby(df['id']).bfill(3).fillna(0)
Out[223]: 
0     0.0
1     0.0
2     0.0
3     1.0
4     1.0
5     1.0
6     1.0
7     1.0
8     1.0
9     1.0
10    1.0
11    0.0
12    0.0
13    0.0
14    1.0
15    1.0
16    0.0
17    1.0
18    1.0
19    1.0
20    1.0
Name: v1, dtype: float64
#df['v2']=df.v1.where(df.v1==1).groupby(df['id']).bfill(3).fillna(0)
  • 0
    Для людей, просматривающих этот пост в будущем, это сработало для меня в Python. Спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню