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, and custom validation.
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
| Chat style | Traditional with images | Mobile optimized |
|---|---|---|
![]() | ![]() | ![]() |
| Two-column traditional | Two-column mobile optimized |
|---|---|
![]() | ![]() |
Configuration Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| question | string | yes | - | The prompt shown to the user |
| rows | List[str | MediaItem] | yes | - | Rows to show in the grid |
| row_name | string | yes | - | Reporting label for rows |
| columns | List[str] | no | - | Column labels |
| column_name | string | no | - | Reporting label for columns |
| image | MediaItem | no | - | Top-level image |
| min_max | Tuple[int, int] | no | (1, 10) | Inclusive range of acceptable numeric values |
| randomize | bool | no | False | Randomize row order |
| randomize_columns | bool | no | False | Randomize column order |
| recodes | Dict[str, str] | no | - | Optional recoding logic |
| default | Dict[str, int | Dict[str, int]] | List[Dict] | no | random | Test-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 |
| autosum_columns | bool | no | False | Require responses to sum correctly by column |
| autosum_rows | bool | no | False | Require responses to sum correctly by row |
| custom_validator | Callable[[Dict[str, int] | int], str | None] | no | straight-line check | Called 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_field | str | no | - | Used to label media row items |
| show_image_label | bool | no | True | Show/hide labels for row images |
| image_size | Tuple[int, int] | no | (600, 600) | Bounding box for images |
| number_seconds | int | no | 0 | Seconds to wait before allowing the respondent to continue |
| tags | s.tag() | no | - | Token substitution and reporting group |
| id | str | no | - | Optional stable identifier for this question |
Example Code
Basic usage:
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:
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:
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:
{
"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:
{
"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:
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
DictResponsecontaining 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






