Stata: подмножество данных с использованием критериев, хранящихся в другом наборе данных
У меня большой набор данных. Я должен установить поднабор данных (Big_data), используя значения, хранящиеся в другом файле dta (Criteria_data). Сначала я покажу вам проблему:
**Big_data** **Criteria_data**
==================== ================================================
lon lat 4_digit_id minlon maxlon minlat maxlat
-76.22 44.27 0765 -78.44 -77.22 34.324 35.011
-67.55 33.19 6161 -66.11 -65.93 40.32 41.88
....... ........
(over 1 million obs) (271 observations)
==================== ================================================
Я должен установить следующие данные ставок:
use Big_data
preserve
keep if (-78.44<lon<-77.22) & (34.324<lat<35.011)
save data_0765, replace
restore
preserve
keep if (-66.11<lon<-65.93) & (40.32<lat<41.88)
save data_6161, replace
restore
....
(1) Каким должно быть эффективное программирование для поднабора в Stata? (2) Правильно ли написаны выражения неравенства?
3 ответа
1) Подмножество данных
С 400000 наблюдений в основном файле и 300 в справочном файле это занимает около 1,5 минут. Я не могу проверить это с двойным наблюдением в основном файле, потому что нехватка ОЗУ приводит мой компьютер к ползанию.
Стратегия предусматривает создание столько переменных, сколько необходимо для хранения опорных широт и долгот (271*4 = 1084 в случае OP; Stata IC и выше могут справиться с этим. См. help limits
). Это требует некоторого изменения формы и добавления. Затем мы проверяем те наблюдения файла больших данных, которые соответствуют условиям.
clear all
set more off
*----- create example databases -----
tempfile bigdata reference
input ///
lon lat
-76.22 44.27
-66.0 40.85 // meets conditions
-77.10 34.8 // meets conditions
-66.00 42.0
end
expand 100000
save "`bigdata'"
*list
clear all
input ///
str4 id minlon maxlon minlat maxlat
"0765" -78.44 -75.22 34.324 35.011
"6161" -66.11 -65.93 40.32 41.88
end
drop id
expand 150
gen id = _n
save "`reference'"
*list
*----- reshape original reference file -----
use "`reference'", clear
tempfile reference2
destring id, replace
levelsof id, local(lev)
gen i = 1
reshape wide minlon maxlon minlat maxlat, i(i) j(id)
gen lat = .
gen lon = .
save "`reference2'"
*----- create working database -----
use "`bigdata'"
timer on 1
quietly {
forvalues num = 1/300 {
gen minlon`num' = .
gen maxlon`num' = .
gen minlat`num' = .
gen maxlat`num' = .
}
}
timer off 1
timer on 2
append using "`reference2'"
drop i
timer off 2
*----- flag observations for which conditions are met -----
timer on 3
gen byte flag = 0
foreach le of local lev {
quietly replace flag = 1 if inrange(lon, minlon`le'[_N], maxlon`le'[_N]) & inrange(lat, minlat`le'[_N], maxlat`le'[_N])
}
timer off 3
*keep if flag
*keep lon lat
*list
timer list
inrange()
Функция подразумевает, что минимумы и максимумы должны быть скорректированы заранее, чтобы удовлетворить строгие неравенства ОП (функция тесты <=, >=).
Вероятно, какое-то расширение, используя expand
, использование соответствующих и by
(поэтому данные в полной форме) могут ускорить процесс. Это не совсем понятно для меня сейчас. Я уверен, что в обычном режиме Stata есть лучшие способы. Мата может быть даже лучше.
(joinby
Также был протестирован, но опять-таки ОЗУ было проблемой.)
редактировать
Выполнение вычислений в блоках, а не для всей базы данных, значительно улучшает проблему с оперативной памятью. Используя основной файл с 1,2 миллионами наблюдений и справочный файл с 300 наблюдениями, следующий код выполняет всю работу за 1,5 минуты:
set more off
*----- create example big data -----
clear all
set obs 1200000
set seed 13056
gen lat = runiform()*100
gen lon = runiform()*100
local sizebd `=_N' // to be used in computations
tempfile bigdata
save "`bigdata'"
*----- create example reference data -----
clear all
set obs 300
set seed 97532
gen minlat = runiform()*100
gen maxlat = minlat + runiform()*5
gen minlon = runiform()*100
gen maxlon = minlon + runiform()*5
gen id = _n
tempfile reference
save "`reference'"
*----- reshape original reference file -----
use "`reference'", clear
destring id, replace
levelsof id, local(lev)
gen i = 1
reshape wide minlon maxlon minlat maxlat, i(i) j(id)
drop i
tempfile reference2
save "`reference2'"
*----- create file to save results -----
tempfile results
clear all
set obs 0
gen lon = .
gen lat = .
save "`results'"
*----- start computations -----
clear all
* local that controls # of observations in intermediate files
local step = 5000 // can't be larger than sizedb
timer clear
timer on 99
forvalues en = `step'(`step')`sizebd' {
* load observations and join with references
timer on 1
local start = `en' - (`step' - 1)
use in `start'/`en' using "`bigdata'", clear
timer off 1
timer on 2
append using "`reference2'"
timer off 2
* flag observations that meet conditions
timer on 3
gen byte flag = 0
foreach le of local lev {
quietly replace flag = 1 if inrange(lon, minlon`le'[_N], maxlon`le'[_N]) & inrange(lat, minlat`le'[_N], maxlat`le'[_N])
}
timer off 3
* append to result database
timer on 4
quietly {
keep if flag
keep lon lat
append using "`results'"
save "`results'", replace
}
timer off 4
}
timer off 99
timer list
display "total time is " `r(t99)'/60 " minutes"
use "`results'"
browse
2) Неравенство
Вы спрашиваете, правильны ли ваши неравенства. На самом деле они законны, то есть Stata не будет жаловаться, но результат, вероятно, неожиданный.
Следующий результат может показаться удивительным:
. display (66.11 < 100 < 67.93)
1
Как это так, что выражение оценивается как истинное (т. Е. 1)? Stata сначала оценивает 66.11 < 100
что верно, а потом видит 1 < 67.93
что тоже верно, конечно.
Предполагаемое выражение было (и теперь Stata будет делать то, что вы хотите):
. display (66.11 < 100) & (100 < 67.93)
0
Вы также можете положиться на функцию inrange()
,
Следующий пример согласуется с предыдущим объяснением:
. display (66.11 < 100 < 0)
0
Стата видит 66.11 < 100
что верно (то есть 1) и следует с 1 < 0
, который является ложным (то есть 0).
Это использует настройки данных Роберто:
clear all
set obs 1200000
set seed 13056
gen lat = runiform()*100
gen lon = runiform()*100
local sizebd `=_N' // to be used in computations
tempfile bigdata
save "`bigdata'"
*----- create example reference data -----
clear all
set obs 300
set seed 97532
gen minlat = runiform()*100
gen maxlat = minlat + runiform()*5
gen minlon = runiform()*100
gen maxlon = minlon + runiform()*5
gen id = _n
tempfile reference
save "`reference'"
timer on 1
levelsof id, local(id_list)
foreach id of local id_list {
sum minlat if id==`id', meanonly
local minlat = r(min)
sum maxlat if id==`id', meanonly
local maxlat = r(max)
sum minlon if id==`id', meanonly
local minlon = r(min)
sum maxlon if id==`id', meanonly
local maxlon = r(max)
preserve
use if (inrange(lon,`minlon',`maxlon') & inrange(lat,`minlat',`maxlat')) using "`bigdata'", clear
qui save data_`id', replace
restore
}
timer off 1
Я бы постарался избежать preserve
и restore
"большой" файл, и это возможно, но за счет потери формата Stata.
Используя те же настройки, что и Роберто и Димитрий,
set more off
use `bigdata', clear
merge 1:1 _n using `reference'
* check for data consistency:
* minlat, maxlat, minlon, maxlon are either all defined or all missing
assert inlist( mi(minlat) + mi(maxlat) + mi(minlon) + mi(maxlon), 0, 4)
* this will come handy later
gen byte touse = 0
* set up and cycle over the reference data
count if !missing(minlat)
forvalues n=1/`=r(N)' {
replace touse = inrange(lat,minlat[`n'],maxlat[`n']) & inrange(lon,minlon[`n'],maxlon[`n'])
local thisid = id[`n']
outfile lat lon if touse using data_`thisid'.csv, replace comma
}
Время это на вашей машине. Вы могли бы избежать touse
а также thisid
и только один outfile
в цикле, но это было бы менее читабельным.
Вы можете тогда infile lat lon using data_###.csv, clear
потом. Если вам действительно нужны файлы Stata, вы можете конвертировать этот рой CSV-файлов с помощью
clear
local allcsv : dir . files "*.csv"
foreach f of local allcsv {
* change the filename
local dtaname = subinstr(`"`f'"',".csv",".dta",.)
infile lat lon using `"`f'"', clear
if _N>0 save `"`dtaname'"', replace
}
Время это тоже. Я защищал save
так как некоторые из смоделированных наборов данных были пусты. Я думаю, что это было быстрее, чем 1,5 минуты на моей машине, включая преобразование.