Мои данные
Я работаю над набором данных заклинаний в следующем формате:
cls
clear all
set more off
input id spellnr str7 bdate_str str7 edate_str employed
1 1 2008m1 2008m9 1
1 2 2008m12 2009m8 0
1 3 2009m11 2010m9 1
1 4 2010m10 2011m9 0
///
2 1 2007m4 2009m12 1
2 2 2010m4 2011m4 1
2 3 2011m6 2011m8 0
end
* translate to Stata monthly dates
gen bdate = monthly(bdate_str,"YM")
gen edate = monthly(edate_str,"YM")
drop *_str
format %tm bdate edate
list, sepby(id)
Соответствует:
+---------------------------------------------+
| id spellnr employed bdate edate |
|---------------------------------------------|
1. | 1 1 1 2008m1 2008m9 |
2. | 1 2 0 2008m12 2009m8 |
3. | 1 3 1 2009m11 2010m9 |
4. | 1 4 0 2010m10 2011m9 |
|---------------------------------------------|
5. | 2 1 1 2007m4 2009m12 |
6. | 2 2 1 2010m4 2011m4 |
7. | 2 3 0 2011m6 2011m8 |
+---------------------------------------------+
Здесь у данного человека (id
) может быть несколько заклинаний (spellnr
) двух типов (unempl
: 1 для безработицы; 0 для занятости).даты начала и окончания каждого заклинания определяются bdate
и edate
, соответственно.
Представьте, что данные уже очищены и таковы, что никакие заклинания не перекрываются друг с другом,Между любыми двумя заклинаниями могут быть «пропущенные» периоды.Это отражено в фиктивном наборе данных, приведенном выше.
Мой вопрос:
Для каждого периода безработицы мне нужно вычислить количество месяцев, потраченных на занятость за последние 6 месяцев, 12 месяцев и 24 месяца.
Обратите внимание, что, что важно, каждый id
может входить и выходить из работы, и все прошлые заклинания занятости должны быть приняты во внимание (не только последнее).
В моем примере это привело бы к следующему желаемому выводу:
+--------------------------------------------------------------+
| id spellnr employed bdate edate m6 m24 m48 |
|--------------------------------------------------------------|
1. | 1 1 1 2008m1 2008m9 . . . |
2. | 1 2 0 2008m12 2009m8 4 9 9 |
3. | 1 3 1 2009m11 2010m9 . . . |
4. | 1 4 0 2010m10 2011m9 6 11 20 |
|--------------------------------------------------------------|
5. | 2 1 1 2007m4 2009m12 . . . |
6. | 2 2 1 2010m4 2011m4 . . . |
7. | 2 3 0 2011m6 2011m8 5 20 44 |
+--------------------------------------------------------------+
Моя (рабочая) попытка:
Следующий код возвращает желаемый результат,
* expand each spell to one observation per time unit (here "months"; works also for days)
expand edate-bdate+1
bysort id spellnr: gen spell_date = bdate + _n - 1
format %tm spell_date
list, sepby(id spellnr)
* fill-in empty months (not covered by spells)
xtset id spell_date, monthly
tsfill
* compute cumulative time spent in employment and lagged values
bysort id (spell_date): gen cum_empl = sum(employed) if employed==1
bysort id (spell_date): replace cum_empl = cum_empl[_n-1] if cum_empl==.
bysort id (spell_date): gen lag_7 = L7.cum_empl if employed==0
bysort id (spell_date): gen lag_24 = L25.cum_empl if employed==0
bysort id (spell_date): gen lag_48 = L49.cum_empl if employed==0
qui replace lag_7=0 if lag_7==. & employed==0 // fix computation for first spell of each "id" (if not enough time to go back with "L.")
qui replace lag_24=0 if lag_24==. & employed==0
qui replace lag_48=0 if lag_48==. & employed==0
* compute time spent in employment in the last 6, 24, 48 months, at the beginning of each unemployment spell
bysort id (spell_date): gen m6 = cum_empl - lag_7 if employed==0
bysort id (spell_date): gen m24 = cum_empl - lag_24 if employed==0
bysort id (spell_date): gen m48 = cum_empl - lag_48 if employed==0
qui drop if (spellnr==.)
qui bysort id spellnr (spell_date): keep if _n == 1
drop spell_date cum_empl lag_*
list
Это прекрасно работает, но становится совершенно неэффективным при использовании (нескольких миллионов) ежедневных данных.Можете ли вы предложить какой-либо альтернативный подход, который не предусматривает расширение набора данных?
На словах я делаю выше:
- Я расширяю данные, чтобы иметь одну строку в месяц;
- Я заполняю «промежутки» между заклинаниями с помощью
-tsfill-
- I Вычисляю время работы, потраченное на работу, и использую операторы запаздывания для получения трех интересующих величин.
Это в духе того, что было сделано здесь в прошлом вопросе, который я разместил.Однако рабочий пример там оказался излишне сложным и с некоторыми ошибками.
РАБОТА С РЕШЕНИЯМИ
Я пробовал разные подходы, предложенные в принятом ответе ниже (включая использование joinby
, как предлагалось в более ранней версии ответа).Чтобы создать больший набор данных, я использовал:
expand 500000
bysort id spellnr: gen new_id = _n
drop id
rename new_id id
, который создает набор данных с 500 000 идентификаторов (всего 3 500 000 заклинаний).Первое решение в значительной степени доминирует над теми, которые используют joinby
или rangejoin
(см. Также комментарии к принятому ответу ниже).