messenger_logo
Liên hệ qua Messenger
SciEco

Lập trình lệnh ước lượng trong stata: kích hoạt chức năng predict

I
IEFPA
Ngày viết: 25/05/2026

Trong lập trình các lệnh ước lượng tùy chỉnh trong Stata, việc đảm bảo chức năng `predict` hoạt động chính xác sau khi chạy mô hình là một yếu tố quan trọng để người dùng có thể trích xuất các dự đoán hiệu quả. Bài viết này sẽ hướng dẫn cách xây dựng một ado-command riêng biệt để tính toán các giá trị dự đoán và tích hợp nó vào lệnh ước lượng chính, từ đó mở rộng khả năng phân tích dữ liệu.

Một Lệnh Ado Để Tính Toán Dự Đoán

Theo quy ước, một ado-command dùng để tính toán các dự đoán cho lệnh ước lượng `mytest` sẽ được đặt tên là `mytest_p`. Trong trường hợp này, chúng ta sẽ xem xét `mypoisson5_p`, ado-command chịu trách nhiệm tính toán dự đoán sau khi chạy lệnh `mypoisson5`.

Cú pháp của `mypoisson5_p` được định nghĩa như sau:

`mypoisson5_p [type] newvarname [if] [in] [, n xb]`

Lệnh `mypoisson5_p` sẽ tính toán số lượng đếm dự kiến khi tùy chọn `n` được chỉ định, và tính toán các dự đoán tuyến tính khi tùy chọn `xb` được chỉ định. `n` là tùy chọn mặc định nếu người dùng không chỉ định cả `xb` và `n`. Điều quan trọng cần lưu ý là người dùng không được phép chỉ định đồng thời cả `xb` và `n`.

Dưới đây là mã nguồn cho ado-command này.

1program define mypoisson5_p
2    version 14
3    syntax newvarname [if] [in] , [ xb n ]
4    marksample touse, novarlist
5    local nopts : word count `xb' `n'
6    if `nopts' >1 {
7        display "{err}chỉ có thể chỉ định một thống kê"
8        exit 498
9    }
10    if `nopts' == 0 {
11        local n n
12        display "số lượng đếm dự kiến"
13    }
14    if "`xb'" != "" {
15        _predict `typlist' `varlist' if `touse' , xb
16    }
17    else {
18        tempvar xbv
19        quietly _predict double `xbv' if `touse' , xb
20        generate `typlist' `varlist' = exp(`xbv') if `touse'
21    }

end

Dòng thứ 5 sử dụng lệnh `syntax` để phân tích cú pháp được người dùng cung cấp. Lệnh này chỉ định rằng `mypoisson5_p` yêu cầu tên của một biến mới, cho phép điều kiện `if` hoặc `in`, và chấp nhận các tùy chọn `xb` và `n`. `syntax newvarname` đảm bảo người dùng phải cung cấp một tên cho biến mới không tồn tại trong tập dữ liệu đang được sử dụng. `syntax` sẽ lưu tên biến mới này vào macro cục bộ `varlist`. Nếu người dùng chỉ định một loại biến (ví dụ `double`) ngoài tên biến, loại biến đó sẽ được lưu vào macro cục bộ `typlist`. Chẳng hạn, nếu người dùng nhập:

1mypoisson5_p double yhat

thì macro cục bộ `varlist` sẽ chứa "yhat" và `typlist` sẽ chứa "double". Nếu không có loại biến nào được chỉ định, `typlist` sẽ trống.

Dòng thứ 7 sử dụng `marksample` để tạo một biến nhận diện mẫu, với tên được lưu trong macro cục bộ `touse`. Ở đây, tùy chọn `novarlist` được chỉ định để `marksample` chỉ sử dụng các điều kiện `if` hoặc `in` do người dùng cung cấp để tạo biến nhận diện mẫu, mà không tính đến các quan sát không tồn tại trong biến mới.

Các tùy chọn `xb` và `n` chỉ định thống kê cần tính toán. Lệnh `syntax` ở dòng 5 cho phép người dùng chỉ định:

1. Tùy chọn `xb`

2. Tùy chọn `n`

3. Cả `xb` và `n`

4. Không `xb` cũng không `n`

Trong trường hợp (1), macro cục bộ `xb` sẽ chứa "xb" và `n` sẽ trống. Trong trường hợp (2), `xb` sẽ trống và `n` sẽ chứa "n". Trong trường hợp (3), cả `xb` và `n` đều chứa giá trị tương ứng. Trong trường hợp (4), cả hai macro đều trống. Biểu đồ cú pháp và mô tả ngụ ý rằng các trường hợp (1), (2) và (4) là hợp lệ, trong khi trường hợp (3) sẽ là lỗi. Dòng 9 lưu số lượng tùy chọn được người dùng chỉ định vào macro cục bộ `nopts`. Phần còn lại của mã sử dụng `nopts`, `xb` và `n` để xử lý các trường hợp (1) đến (4).

Các dòng 10–13 xử lý trường hợp (3) bằng cách thoát với thông báo lỗi lịch sự khi `nopts` chứa 2. Các dòng 15–18 xử lý trường hợp (4) bằng cách gán "n" vào macro cục bộ `n` khi `nopts` chứa 0.

Tại thời điểm này, chúng ta đã xử lý các trường hợp (3) và (4), và sẽ sử dụng `xb` và `n` để xử lý các trường hợp (1) và (2), vì hoặc `xb` không trống và `n` trống, hoặc `xb` trống và `n` không trống.

Các dòng 20–22 xử lý trường hợp (1) bằng cách sử dụng `_predict` để tính toán các dự đoán `xb` khi macro cục bộ `xb` không trống. Lưu ý rằng các dự đoán được tính toán với độ chính xác do người dùng chỉ định. Các dòng 23–27 xử lý trường hợp (2) bằng cách sử dụng `_predict` để tính `xb` vào một biến tạm thời, sau đó biến này được dùng để tính `n`. Lưu ý rằng biến tạm thời cho `xb` luôn được tính với độ chính xác kép (double precision) và `n` được tính với độ chính xác do người dùng chỉ định.

Lưu Tên Lệnh Dự Đoán Vào E(Predict)

Để người dùng có thể tính toán thống kê `xb` bằng cách nhập:

1predict double yhat, xb

thay vì phải gõ:

1mypoisson5_p double yhat, xb

Cú pháp `predict` tiêu chuẩn này hoạt động vì lệnh `predict` sẽ sử dụng ado-command có tên được lưu trong macro `e(predict)`. Trong dòng 50 của `mypoisson5` (khối mã bên dưới), chúng ta lưu "mypoisson5_p" vào `e(predict)`. Việc bổ sung này là điểm khác biệt duy nhất giữa `mypoisson5.ado` trong khối mã này và `mypoisson4.ado` đã được thảo luận trong các bài viết trước.

1program define mypoisson5, eclass sortpreserve
2    version 14
3    syntax varlist(numeric ts fv min=2) [if] [in] [, noCONStant vce(string) ]
4    marksample touse
5    _vce_parse `touse' , optlist(Robust) argoptlist(CLuster) : , vce(`vce')
6    local vce        "`r(vce)'"
7    local clustervar "`r(cluster)'"
8    if "`vce'" == "robust" | "`vce'" == "cluster" {
9        local vcetype "Robust"
10    }
11    if "`clustervar'" != "" {
12        capture confirm numeric variable `clustervar'
13        if _rc {
14            display in red "invalid vce() option"
15            display in red "cluster variable {bf:`clustervar'} is " ///
16                "string variable instead of a numeric variable"
17            exit(198)
18        }
19        sort `clustervar'
20    }
21    gettoken depvar indepvars : varlist
22    _fv_check_depvar `depvar'
23    tempname b mo V N rank
24    getcinfo `indepvars' , `constant'
25    local  cnames "`r(cnames)'"
26    matrix `mo' = r(mo)
27    mata: mywork("`depvar'", "`cnames'", "`touse'", "`constant'", ///
28       "`b'", "`V'", "`N'", "`rank'", "`mo'", "`vce'", "`clustervar'")
29    if "`constant'" == "" {
30        local cnames "`cnames' _cons"
31    }
32    matrix colnames `b' = `cnames'
33    matrix colnames `V' = `cnames'
34    matrix rownames `V' = `cnames'
35    ereturn post `b' `V', esample(`touse') buildfvinfo
36    ereturn scalar N       = `N'
37    ereturn scalar rank    = `rank'
38    ereturn local  vce      "`vce'"
39    ereturn local  vcetype  "`vcetype'"
40    ereturn local  clustvar "`clustervar'"
41    ereturn local  predict "mypoisson5_p"
42    ereturn local  cmd     "mypoisson5"
43    ereturn display

program getcinfo, rclass

syntax varlist(ts fv), [ noCONStant ]

_rmcoll `varlist' , `constant' expand

local cnames `r(varlist)'

local p : word count `cnames'

if "`constant'" == "" {

local p = `p' + 1

local cons _cons

}

tempname b mo

matrix `b' = J(1, `p', 0)

matrix colnames `b' = `cnames' `cons'

_ms_omit_info `b'

matrix `mo' = r(omit)

return local cnames "`cnames'"

return matrix mo = `mo'

end

mata:

void mywork( string scalar depvar, string scalar indepvars,

string scalar touse, string scalar constant,

string scalar bname, string scalar Vname,

string scalar nname, string scalar rname,

string scalar mo,

string scalar vcetype, string scalar clustervar)

{

real vector y, b

real matrix X, V, Ct

real scalar n, p, rank

y = st_data(., depvar, touse)

n = rows(y)

X = st_data(., indepvars, touse)

if (constant == "") {

X = X,J(n, 1, 1)

}

p = cols(X)

Ct = makeCt(mo)

S = optimize_init()

optimize_init_argument(S, 1, y)

optimize_init_argument(S, 2, X)

optimize_init_evaluator(S, &plleval3())

optimize_init_evaluatortype(S, "gf2")

optimize_init_params(S, J(1, p, .01))

optimize_init_constraints(S, Ct)

b = optimize(S)

if (vcetype == "robust") {

V = optimize_result_V_robust(S)

}

else if (vcetype == "cluster") {

cvar = st_data(., clustervar, touse)

optimize_init_cluster(S, cvar)

V = optimize_result_V_robust(S)

}

else { // vcetype must IID

V = optimize_result_V_oim(S)

}

rank = p - diag0cnt(invsym(V))

st_matrix(bname, b)

st_matrix(Vname, V)

st_numscalar(nname, n)

st_numscalar(rname, rank)

}

real matrix makeCt(string scalar mo)

{

real vector mo_v

real scalar ko, j, p

mo_v = st_matrix(mo)

p = cols(mo_v)

ko = sum(mo_v)

if (ko>0) {

Ct = J(0, p, .)

for(j=1; j<=p; j++) {

if (mo_v[j]==1) {

Ct = Ct \ e(j, p)

}

}

Ct = Ct, J(ko, 1, 0)

}

else {

Ct = J(0,p+1,.)

}

return(Ct)

}

void plleval3(real scalar todo, real vector b, ///

real vector y, real matrix X, ///

val, grad, hess)

{

real vector xb, mu

xb = X*b'

mu = exp(xb)

val = (-mu + y:*xb - lnfactorial(y))

if (todo>=1) {

grad = (y - mu):*X

}

if (todo==2) {

hess = -quadcross(X, mu, X)

}

}

end

end

Ví Dụ Thực Hành

Ví dụ sau minh họa rằng việc triển khai của chúng ta hoạt động bằng cách so sánh các dự đoán thu được sau khi chạy `mypoisson5` với các dự đoán thu được sau khi chạy lệnh `poisson` tích hợp sẵn của Stata.

1clear all
2use accident3
3quietly poisson accidents cvalue kids traffic
4predict double n1
5(option n assumed; predicted number of events)
6quietly mypoisson5 accidents cvalue kids traffic
7predict double n2
8expected counts
9list n1 n2 in 1/5
10     +-----------------------+
11     |        n1          n2 |
12     |-----------------------|
13  1. | .15572052   .15572052 |
14  2. | .47362502   .47362483 |
15  3. | .46432954   .46432946 |
16  4. | .84841301   .84841286 |
17  5. | .40848207   .40848209 |
18     +-----------------------+

Các kết quả gần như giống hệt nhau, cho thấy rằng `predict` đã hoạt động chính xác sau lệnh `mypoisson5` tùy chỉnh của chúng ta.

✨ Khả năng tích hợp chức năng `predict` vào các lệnh ước lượng tùy chỉnh trong Stata là vô cùng giá trị. Nó không chỉ giúp người dùng dễ dàng trích xuất các dự đoán phù hợp với mô hình độc đáo của họ mà còn biến một lệnh ước lượng cơ bản thành một giải pháp phân tích hoàn chỉnh, nâng cao đáng kể hiệu quả và tính linh hoạt trong công việc khoa học dữ liệu.

1. Hãy suy nghĩ về cách bạn có thể mở rộng `mypoisson5_p` để tính toán khoảng tin cậy cho các dự đoán số lượng đếm. Bạn sẽ cần những thông tin nào từ kết quả ước lượng chính và các hàm nào của Stata sẽ hữu ích?

2. Nếu bạn cần hỗ trợ thêm các loại dự đoán khác (ví dụ: xác suất sự kiện, phần dư) cho lệnh ước lượng tùy chỉnh, bạn sẽ cấu trúc lệnh `_p` của mình như thế nào để xử lý đa dạng các tùy chọn này một cách hiệu quả?


Bài viết khác
Bài viết này khám phá cách cập nhật và tạo các lệnh Stata để tích hợp với những mô hình AI phổ biến như ChatGPT, Claude, Gemini và Grok. Sau khi bài đăng trước về lệnh Stata để chạy ChatGPT trở nên phổ biến, những thay đổi trong mã API của OpenAI đã khiến lệnh đó không còn hoạt động. Mục tiêu của chúng tôi là hướng dẫn bạn cách điều chỉnh mã API và viết các lệnh Stata tương tự để tận dụng sức mạnh của các công cụ AI khác nhau trực tiếp từ môi trường Stata. Trọng tâm của bài viết này, cũng như bài trước, là minh họa mức độ dễ dàng để tận dụng các tính năng của PyStata nhằm kết nối với ChatGPT và các công cụ AI khác, thay vì đưa ra lời khuyên về cách sử dụng các công cụ AI để trả lời các câu hỏi cụ thể của Stata. Do đó, các ví dụ chỉ đơn giản là yêu cầu một bài haiku về Stata. Tuy nhiên, bạn có thể truyền bất kỳ yêu cầu nào mà bạn thấy hữu ích trong quy trình làm việc của mình với Stata. Tổng Quan Về Tích Hợp Stata/Python Chúng tôi giả định rằng bạn đã quen thuộc với tích hợp Stata/Python và cách viết lệnh chatgpt ban đầu. Nếu các chủ đề này còn lạ lẫm, bạn nên đọc các bài đăng trên blog dưới đây:
Trong thế giới khoa học dữ liệu hiện đại, việc phân tích các bộ dữ liệu khổng lồ và phức tạp là một thách thức không ngừng. Các nhà nghiên cứu thường phải làm việc với nhiều bộ dữ liệu liên quan cùng một lúc, đòi hỏi các công cụ quản lý dữ liệu hiệu quả và linh hoạt. Stata, với lịch sử lâu đời trong việc xử lý và phân tích dữ liệu, đã liên tục đổi mới để đáp ứng những nhu cầu này. Stata 18, ra mắt vào năm 2023, tiếp tục nâng cao khả năng này bằng cách giới thiệu hai tính năng đột phá: framesets và biến số bí danh giữa các frames. Những cải tiến này cho phép người dùng quản lý và tương tác với nhiều bộ dữ liệu, có thể rất lớn, một cách hiệu quả và thuận tiện hơn bao giờ hết. Framesets cho phép bạn nhóm, lưu trữ trên ổ đĩa và tải vào bộ nhớ một tập hợp các frames chứa bộ dữ liệu liên quan. Biến số bí danh cho phép bạn truy cập các biến số trong các frames khác như thể chúng là một phần của frame hiện tại, với chi phí bộ nhớ rất thấp. Bài viết này sẽ đi sâu vào cách những tính năng này cách mạng hóa quy trình làm việc dữ liệu trong Stata. Quản Lý Dữ Liệu Trong Stata: Hành Trình Phát Triển Từ khi Stata 1.0 ra đời vào năm 1985, dữ liệu đã được tổ chức dưới dạng bảng với các quan sát (hàng) và biến số (cột), được gọi là bộ dữ liệu. Các bộ dữ liệu này được lưu trữ hoàn toàn trong bộ nhớ và ghi vào ổ đĩa dưới dạng tệp .dta. Các kiểu dữ liệu, như số nguyên, số thực và đặc biệt là chuỗi, được quản lý một cách tiết kiệm. Hầu hết các lệnh ban đầu của Stata, bao gồm generate, replace và list không thể thiếu, đều tập trung vào quản lý dữ liệu.
SciEco
Science for Economics
Định hướng đào tạo phân tích dữ liệu, xây dựng chính sách, tối ưu hoá danh mục tài chính cá nhân và dự báo thị trường.
Liên hệ
Địa chỉ: Số 60, ngõ 41, Phố Thái Hà, Trung Liệt, Đống Đa, Hà Nội (Google Map)
Email: science.for.economics@gmail.com
Hotline: 03.57.94.7680 (Mrs. Hà)
Mạng xã hội