Documentation

Numeric Grid Question

When and Why to Use

Use this to capture numeric input across a grid of rows and columns. Best for:

  • Time allocation or quantity distribution
  • Budget breakdowns
  • Structured numeric input across categories

Supports autosumming, recoding, custom validation and editable starting values.

Other Names and Formats

A Numeric Grid Question is a numeric matrix, numeric-entry grid, or numeric textbox grid. Budget and time distribution tasks are also called allocation questions, budget allocation, or time allocation questions.

Continuous sum is a related name used for numeric entries summed across items. A constant-sum allocation question additionally requires the survey to enforce a specified total, such as 100 points. Configure and verify that total constraint for the intended survey experience; autosumming alone does not establish a fixed-total requirement.

Chat Style

  • Each row is presented with numeric input fields for each column
  • Users enter numbers directly

Traditional Style

  • Full grid visible with scrollable columns if needed
  • Easier comparison across multiple categories
  • Autosums are enabled

Traditional desktop numeric grid with rows A, B and C, two editable value columns and a Send button

The traditional layout keeps both value columns visible alongside each row. Respondents can edit the entries before selecting Send.

Configuration Options

OptionTypeRequiredDefaultDescription
questionstringyes-The prompt shown to the user
rowsList[str | MediaItem]yes-Rows to show in the grid
row_namestringyes-Reporting label for rows
columnsList[str]no-Column labels
column_namestringno-Reporting label for columns
imageMediaItemno-Top-level image
min_maxTuple[int, int]no(1, 10)Inclusive range of acceptable numeric values
randomizeboolnoFalseRandomize row order
randomize_columnsboolnoFalseRandomize column order
recodesDict[str, str]no-Optional recoding logic
defaultDict[str, int | Dict[str, int]] | List[Dict]norandomTest-data defaults keyed by row (or by row, then column, when columns are used), or a list of candidate dictionaries — each simulated respondent picks one at random
prefillint | dict[str, int] | dict[str, dict[str, int]] | NonenoNoneEditable respondent starting values, separate from test-data defaults
autosum_columnsboolnoFalseRequire responses to sum correctly by column
autosum_rowsboolnoFalseRequire responses to sum correctly by row
custom_validatorCallable[[Dict[str, int] | int], str | None]nostraight-line checkCalled with the parsed grid dictionary in the traditional experience, or with each parsed number in the chat experience (asked row by row); return an error message to reject
image_label_fieldstrno-Used to label media row items
show_image_labelboolnoTrueShow/hide labels for row images
image_sizeTuple[int, int]no(600, 600)Bounding box for images
number_secondsintno0Seconds to wait before allowing the respondent to continue
tagss.tag()no-Token substitution and reporting group
idstrno-Optional stable identifier for this question

Editable starting values

Use prefill when respondents should see a starting number that they can review and change. This does not submit an answer by itself. It is separate from default, which supplies test/simulation responses and the tester's Auto answer.

  • Pass one integer to start every cell at that value, for example prefill=0 with a range that permits zero.
  • With no columns, pass a complete dictionary keyed by row.
  • With columns, pass a complete row-first dictionary whose values are dictionaries keyed by column. This shape is the same for prefills in every supported survey version.

Every row and column must be represented, and starting values must be integers within min_max. Partial dictionaries and unknown row/column keys are rejected. Normal answer validation still applies when the respondent submits.

Code
s.grid_numeric_question( "Review your usual daily hours and change any values that differ", rows=["Work", "Sleep"], row_name="Activity", columns=["Monday", "Tuesday"], column_name="Day", min_max=(0, 24), prefill={ "Work": {"Monday": 8, "Tuesday": 8}, "Sleep": {"Monday": 7, "Tuesday": 7}, }, )

A numeric list uses the same function without columns, for example rows=["Food", "Housing"], min_max=(0, 100) and prefill={"Food": 25, "Housing": 75}. Check the range and any total constraint for your questionnaire.

Example Code

Basic usage:

Code
s.grid_numeric_question( "How many hours do you spend per week on the following activities?", row_name="Activity", rows=["Work", "Sleep", "Exercise", "Socializing"], columns=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] )

With recodes and autosum:

Code
s.grid_numeric_question( "Distribute your budget across categories", row_name="Category", rows=["Food", "Housing", "Entertainment"], columns=["January", "February", "March"], recodes={ "0-30%": "Low", "31-70%": "Medium", "71-100%": "High" }, autosum_rows=True )

Response shape by survey version

For version 4 surveys, a multi-column numeric grid returns a row-first DictResponse. The outer keys are row labels and each value is a dictionary keyed by column:

Code
response = s.grid_numeric_question( "How many hours do you spend per day on each activity?", rows=["Work", "Sleep"], row_name="Activity", columns=["Monday", "Tuesday"], column_name="Day", ) for activity, days in response.items(): for day, value in days.items(): s.store_value( "Stored numeric-grid value", value, tags=s.tag(Activity=activity, Day=day), )

The shape is:

json
{ "Work": {"Monday": 8, "Tuesday": 8}, "Sleep": {"Monday": 7, "Tuesday": 7}, }

In version 2 and version 3 surveys, the return shape is column-first, with columns outside and rows inside:

json
{ "Monday": {"Work": 8, "Sleep": 7}, "Tuesday": {"Work": 8, "Sleep": 7}, }

Iterate row then column in version 4, and column then row in versions 2 and 3. The default parameter is row-first — keyed by row and then column — in every supported version.

With custom validation:

Code
s.grid_numeric_question( "How many units of each product did you sell?", row_name="Product", rows=["Item A", "Item B"], columns=["Online", "In-store"], custom_validator=lambda d: "Please don't enter the same number for every cell" if len(dict.fromkeys([v for row in d.values() for v in row.values()])) == 1 else None )

Notes

  • Returns a DictResponse containing the entered numbers; version 4 multi-column grids are row-first, while versions 2 and 3 are column-first
  • Use autosum_columns or autosum_rows to require entries that sum correctly by column or by row
  • Recodes are especially helpful for analysis of numeric ranges
  • A straight-line check is applied by default — pass your own custom_validator to replace it