Don't know and refused answers: checking their share in survey data

Don't know and refused answers are a normal part of survey data, but their share says a lot about how each interview was done. Code them the same way in every question (for example -98 for don't know and -99 for refused), then compute each day the share of answered questions that carry those codes, by interview, by enumerator and by question. A high share on one question usually points to the question; a share well above or well below the team's for one enumerator points to how they ask.

By Mehrab Ali, founder of DataFlow · Updated 7 November 2026

Why the share matters

A respondent who can't remember last month's spending, or who doesn't want to talk about income, should be able to say so, and the data should record it. These answers are information: a question that most people can't answer, or won't, tells you something about the question and the people asked. What makes them worth checking is that the share also depends on the enumerator. One who accepts the first “don't know” without probing (asking again in another way, or helping the respondent work the answer out) collects many more than a colleague who probes. One who pushes for an answer, or guesses, collects fewer. Someone filling in interviews away from the respondent tends to avoid them altogether.

The World Bank's DIME Wiki lists the percentage of “don't know” responses among its enumerator checks in high-frequency checks, and notes that a high share may mean the questions need re-drafting. That is the two-sided reading this page is about: first the questions, then the people. It belongs in the daily high-frequency checks, next to missing answers.

The codes matter for analysis too. If a don't-know code such as -99 stays in an income variable as a number, every mean, total and regression that uses it is wrong, and an outlier check will either flag it every day or have its limits pulled about by it. The codes have to be recognised everywhere the data is used.

Coding them in the form

Pick one set of codes for the whole questionnaire and write it into the codebook before the pilot. The codes should be values that can't be real answers: negative codes work for counts, ages and amounts, which can't be negative. Keep “don't know” and “refused” apart, because they mean different things and lead to different follow-up. Some teams use -98 and -99, others -999 and -888; which you choose matters less than using the same codes in every question, every form version and every round.

In XLSForm, put the codes in the choice lists of single-choice questions, and allow them in the constraints of number questions, with a hint so the enumerator knows what to type:

typenamelabelhintconstraint
integerland_acresHow much land does the household own, in acres?-98 if they don't know, -99 if they refuse(. >= 0 and . <= 500) or . = -98 or . = -99
select_one yndkhas_loanDoes anyone in the household have a loan now?
select_multiple income_srcincome_srcWhere did the household's income come from in the last 12 months?not(selected(., '-98') and count-selected(.) > 1)
list_namenamelabel
yndk1Yes
yndk0No
yndk-98Don't know
yndk-99Refused

The constraint on the multiple-choice question stops “don't know” being ticked alongside real answers. Where a number can legitimately be negative, such as a business's profit, no code is safe: ask first whether the respondent can give the amount, with a single-choice question that has the don't-know and refused options, and ask for the number only when they can.

The codes travel through the form's logic, so check the places they reach. A don't-know on a filter question skips any section that is asked only when the answer is “yes”; decide whether that is what you want. A code in a number that other questions use breaks their constraints and calculations: a household size of -98 makes a constraint such as . <= ${hhsize} impossible for every later count, so write it as . <= ${hhsize} or ${hhsize} < 0, and keep codes out of calculated totals. Enter the codes on purpose during the pilot to test each of these paths.

Decide, and train, whether “don't know” is read out as an option or recorded only when the respondent says it unprompted. Either rule can be right for a study, but a team where some enumerators read it out and others don't will produce very different shares for the same question, and the check will compare habits instead of interviews.

Computing the share in Stata

Count the codes over the questions that can hold them, and divide by the questions answered, so an interview that skipped a long section by design isn't compared on the same footing as one that didn't. Leave out interviews that ended early by design (refusals, nobody at home). Here the numeric questions are named q_*:

* Share of don't know (-98) and refused (-99) answers per interview
use "survey.dta", clear
keep if consent == 1
egen dk       = anycount(q_*), values(-98)
egen refused  = anycount(q_*), values(-99)
egen answered = rownonmiss(q_*)
generate share = (dk + refused) / answered if answered >= 5

* Interviews where more than a fifth of the answers are codes
list hhid enum_id dk refused answered share if share > 0.2 & !missing(share), noobs

* Each enumerator's share of all the answers they recorded
preserve
generate one = 1
collapse (sum) interviews=one dk refused answered, by(enum_id)
generate share = (dk + refused) / answered
gsort -share
list enum_id interviews dk refused share, noobs
restore

Then look at the questions themselves, for everyone and enumerator by enumerator:

* Which questions collect the most codes
foreach q of varlist q_* {
    quietly count if inlist(`q', -98, -99)
    local codes = r(N)
    quietly count if !missing(`q')
    if r(N) >= 20 display "`q'" _col(24) %5.1f 100 * `codes' / r(N) "%  of " r(N)
}

* One question, enumerator by enumerator
generate dk_income = inlist(income_month, -98, -99) if !missing(income_month)
tabstat dk_income, by(enum_id) statistics(mean n)

Before any analysis, turn the codes into Stata's extended missing values. Stata's mvdecode changes the numbers you list into missing-value codes, either the plain . or one of .a to .z. Using .d for don't know and .r for refused keeps the reason while taking the codes out of means and totals:

mvdecode q_* income_month, mv(-98=.d \ -99=.r)
summarize income_month         // the codes no longer count as amounts
count if income_month == .d    // and are still there when you need them

Reading the share fairly

Start with the questions, because a question that most respondents can't answer will push up the share of every enumerator who asks it. Recall questions over a long period, amounts in a unit people don't use, ages of older relatives, and anything translated awkwardly all collect more “don't know” answers. Some questions are hard by nature, and a high share there is a finding about the question, not a fault.

Then compare enumerators, with the same care as any other enumerator monitoring:

What to do with each finding

What you seeLikely causesWhat to do
One question high for everyoneRecall period too long, an unfamiliar unit, a poor translationRe-word it, change the unit or recall period, or add a probing instruction; note the form version that changed it
One enumerator high across the formNot probing, accepting the first “don't know”, reading it out as an optionGo through specific interviews with them, practise probing, have the supervisor sit in on a few interviews, and watch the next days
One enumerator high on one moduleThat part of the training was misunderstoodRetrain on that module only
Refusals gathered in a sensitive moduleNo privacy during the interview, consent rushed, a topic people avoidCheck that interviews are done in private and consent is explained; accept the refusals that are genuine
One interview with many codesA tired or unwilling respondent, a proxy respondent, an interrupted interviewIf key outcome questions are affected, call back or revisit for those questions; otherwise confirm it as it is
One enumerator with almost noneAnswers pushed for, guessed or not askedLook at their interview lengths and other signs, and back-check a sample of their interviews

Never replace a don't-know code with a value at the desk. A code changes only when the respondent gives the answer on a call-back or revisit, and then as a logged correction: the interview, the question, the old and new value, who changed it and why. Keep the raw file as it arrived. The same codes have to be kept out of outlier checks and range checks, or each one becomes a flag every day.

How DataFlow checks don't-know and refused answers

DataFlow reads the codes from the form's choice lists: any choice whose label mentions “know”, “refus” or “dk”, and any negative code whose label contains “not”. When the form has none, it uses -999, -998, -888, -99, -98 and -97. On import it adds the check “Not too many ‘don't know’ answers” when the form has at least five questions that can hold those codes: number questions, and single-choice questions whose list includes one, leaving out ID variables and the interview length. It flags an interview when more than 20% of the answered questions among them are don't-know or refused codes, once at least 5 are answered. It looks only at completed interviews, by the rule DataFlow finds from the form (such as consent given), which can be changed in Settings. The share, the minimum answered, the codes and the list of questions can all be edited. It starts switched off if it would flag more than 5% of interviews, unless at least half of its flags come from one enumerator.

In the Today list, when one enumerator has at least four of these flags and at least half of them, DataFlow shows one card, “Most ‘don't know’ answers are” theirs, with a note to copy for their supervisor: it asks them to go over probing with the enumerator and to revisit the listed respondents where key answers are missing. The card comes back after two days. The other flagged interviews appear together, with the suggestion to revisit the ones where key answers are missing.

The opposite pattern is one of DataFlow's six signs of made-up interviews: don't-know and refusal codes (-999, -998, -888, -99, -98, -97) per interview, below the team's. It is computed for each enumerator with at least 10 interviews, when at least four enumerators have that many to compare, and added to the other five signs; an enumerator whose total is over 8 gets a card asking for their interviews to be back-checked. The suggested outlier and range checks ignore the form's don't-know codes. In a Stata download, the codes from the form become extended missing values (.d for don't know, .r for refused, .n for not applicable, or -999 as .d and -888 as .r when the form names none), with labels, and the do-file that rebuilds the data from the raw file does the same with mvdecode. DataFlow works alongside the collection platform: it connects to SurveyLoopr, SurveyCTO, ODK Central and KoboToolbox, and reads exports from other tools.

Questions

What share of don't know answers is too high in a survey?

There is no single figure: it depends on the questions. Compare each interview and each enumerator with the rest of the team on the same questions. As a starting point, DataFlow flags an interview where more than 20% of the answered questions that allow the codes are don't know or refused, once at least five are answered, and the threshold can be changed.

How should I code don't know and refused answers?

Use one pair of codes for the whole questionnaire, values that can't be real answers, such as -98 for don't know and -99 for refused. Put them in the choice lists of single-choice questions, allow them in the constraints of number questions, keep the two apart, and write them into the codebook.

Should don't know be read out as an option?

Either rule can suit a study, but every enumerator must follow the same one. If some read it out and others record it only when the respondent says it unprompted, the shares will differ between enumerators for reasons that have nothing to do with the respondents.

How do I stop -99 codes from distorting averages in Stata?

Convert them to extended missing values before analysis, for example mvdecode q_*, mv(-98=.d \ -99=.r). They then drop out of means and totals, and you can still count them with if var == .d.

Is a very low share of don't know answers a problem?

It can be. When colleagues asking the same questions record several per interview and one enumerator records almost none, they may be pushing for answers, guessing or not asking. Look at their other measures and back-check a sample of their interviews.

Read next