Use XPath function z-score or pulldata to calculate the specific weight where Z-Score is -1.9, given age and gender?


We currently use the XPath function z-score to calculate weight-for-height, height-for-age, and weight-for-age Z-score in our App.

In one of our forms, we would like to “calculate” ‘target weight’ for malnourished children, which is the weight that a child would reach a weight-for-height Z-score of -1.9 (given gender and height).

Is there a way for us to use or modify the existing Z-score table / z-score function to calculate this target weight? Or perhaps it is better to use something like pulldata (I was looking at this earlier post) and recreate the table?

Thank you for any advice or pointers on the best way forward!


Hi @helenamanguerra-icm

The z-score table supports multiple charts. You could add a separate chart, called target-weight for example, and fill it with appropriate weight values, for gender and height. The documentation you linked to should provide information about how to edit the z-score document in the database.
Then your form would calculate something like z-score('target-weight', ../my_sex, ../my_age, ../my_height) to display the target weight.

Please let us know if this works for you.

Hi Diana, thank you so much for the help! We’ll try this out and let you know if we have issues.

1 Like

Hi Diana, thanks again for your response to this question last month! We’re starting to think through this more carefully, and we’re getting a bit stuck on the solution.

From my understanding, the z-score tables will necessarily give a response between -4 and +4, based on where the “value” fits between the points. For example, in the table below, for a male with age 0, if they have a height of 2.08, then the returned value is -3. (I am not exactly sure how we get exact z-scores, such as -3.8 or -3.7, but it seems like this is the answer).

Is there a way to use the z-score function to give us a response that is not limited to between -4 and 4?

Basically with just sex and height, we want to get the weight in the -2 position. So for this example below (using the weight-for-age instead of weight-for-height chart), since the child is male and age (key) = 0, we would want the weight at -2 position, or 2.459.

Thanks again for any advice!

  "_id": "zscore-charts",
  "charts": [
      "id": "weight-for-age",
      "data": {
        "male": [
            "key": 0,
            "points": [
              1.701,  //-4
              2.08, //-3
              2.459, //-2
              2.881, //-1
              3.346, //0
              3.859, //+1
              4.419, //+2
              5.031, //+3
              5.642 //+4

Hi @helenamanguerra-icm

Lowest value can’t be under -4, because of this calculation:

return lowerIndex + this.MINIMUM_Z_SCORE + ratio;

where lowerIndex is at least 0, MINIMUM_Z_SCORE is -4 and ratio is between 0 and 1.
But, based on this calculation, you can go above 4, if you add more points to your table and force lowerIndex to be above 7.

Now, I’m not sure this is correct :slight_smile: (from the point of view of what z-score actually is).

Got it, thanks for explaining that!

With a better understanding now of how the z-score functions works, I think our team will explore other options for calculating target weight. Do you have advice on how we can implement something like a 4-column (gender, age, height, and target weight) look-up table, where we use a calculate field in our form to input three of the variables (gender, age, height) and are returned the fourth variable (target weight)? Is pulldata the right route?

Many thanks again for helping out with these questions. We really appreciate it!


I believe this is better answered by someone with more experience making forms. I’ll ask around.
From the top of my head, pulldata would be the right route.

It’s technically possible to have the lookup table in the choices tab and query the data using Xpath expressions. This solution was derived from a similar problem posted on the ODK forum.

Here’s a sample choices sheet

list name	name	label::en	age	gender	height	target_weight
gender	male	Male				
gender	female	Female				
zscore	zscore	zscore	0	male	10	1
zscore	zscore	zscore	1	male	20	2
zscore	zscore	zscore	2	male	30	3
zscore	zscore	zscore	0	female	10	4
zscore	zscore	zscore	1	female	20	5
zscore	zscore	zscore	2	female	30	6

and the corresponding survey sheet

type	name	label::en	choice_filter	required	calculation
select_one gender	gender	Gender	true()	yes	
integer	age	Age		yes	
integer	height	Height		yes	
calculate	target_weight				instance('zscore')/root/item[gender=${gender} and age=${age} and height=${height}]/target_weight
note	note_weight	Target Weight: ${target_weight}			

We had to force choice filters so that the actual choices data is stored in the form as a queriable instances and as such accessible via instace('instance-id'). With the above, you should be able to modify the table with the data you need.


Really appreciate you asking around. Thanks again!


Thanks so much Derick! I think we can implement this. Do you anticipate we could run into problems (slow app/calculations, etc) if we have a ~1500 row choices sheet?

I haven’t implemented a similar thing before but there’s a likelihood you may experience slowdowns depending on the devices this survey is deployed to. It may be more noticeable on lower power devices (budget smart phones) compared to PCs. I’d encourage you to share that feedback once your form is fleshed out.

Got it, thanks Derick. Will share back if we experience slowdowns. Thanks again for your help!