Select to view content in your preferred language

Categorizing a Calculated Data Expression Field in Arcade

388
3
Jump to solution
07-26-2024 01:24 PM
DanielleKuchler
Occasional Contributor

Hello Esri Community,

I’m new to using Arcade expressions and I’m currently working on designing a chart element for a dashboard. My goal is to group average scores into 5 distinct categories to improve the clarity of the visual representation in my pie chart.

Here’s what I’ve accomplished so far:

  • I’ve successfully used the created an "averagescore" field (average of 10 different features) in Arcade and used that expression in a pie chart
  • Currently, the pie chart displays all possible values calculated by the average, rather than categories or ranges

DanielleKuchler_0-1722022307637.png

As more data is collected, this setup will likely become cluttered and less effective.

What I Need Help With: I want to categorize the average scores into 5 distinct groups so that the pie chart will be more organized and easier to interpret. Here are example category titles:

  • Score Between 0 and 1.9
  • Score Between 2 and 2.9
  • Score Between 3 and 3.9
  • Score above 4

Is there any way to do this within Arcade? Any advice or suggestions would be appreciated!

 

Here is my code so far:

 

//access steel structure results layer
var p = 'https://www.arcgis.com/'
var itemId = 'xyzxyzxyzxyzx'
var fs = FeatureSetByPortalItem(Portal(p), itemId, 0, ['Leg_1_Ftg_Score', 'Leg_2_Ftg_Score', 'Leg_3_Ftg_Score', 'Leg_4_Ftg_Score', 'Mid_Body_Score', 'Upper_Body_Arms_Score', 'score', 'Coating_Cond_Score', 'groundingscore', 'cathodicprotection_score' ], false);

//create empty dictionary
var fsDict = {
    'fields': [
        {'name': 'Leg_1_Ftg_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Leg_2_Ftg_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Leg_3_Ftg_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Leg_4_Ftg_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Mid_Body_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Upper_Body_Arms_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'score', 'type': 'esriFieldTypeInteger'},
        {'name': 'Coating_Cond_Score', 'type': 'esriFieldTypeInteger'},
        {'name': 'groundingscore', 'type': 'esriFieldTypeInteger'},
        {'name': 'cathodicprotection_score', 'type': 'esriFieldTypeInteger'},
        {'name': 'averagescore', 'type': 'esriFieldTypeDouble'}
    ],
    'geometryType': '',
    'features': []
};

var index = 0;
//loop through features in the featureset to populate dictionary
for (var feature in fs) {
    fsDict.features[index++] = {
        'attributes': {
            'Leg_1_Ftg_Score': feature['Leg_1_Ftg_Score'],
            'Leg_2_Ftg_Score': feature['Leg_2_Ftg_Score'],
            'Leg_3_Ftg_Score': feature['Leg_3_Ftg_Score'],
            'Leg_4_Ftg_Score': feature['Leg_4_Ftg_Score'],
            'Mid_Body_Score': feature['Mid_Body_Score'],
            'Upper_Body_Arms_Score': feature['Upper_Body_Arms_Score'],
            'score': feature['score'],
            'Coating_Cond_Score': feature['Coating_Cond_Score'],
            'groundingscore': feature['groundingscore'],
            'cathodicprotection_score': feature['cathodicprotection_score'],
//calculate average and populate in dictionary
            'averagescore': (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score']))/10
        }
    }
}

//return feature set
return FeatureSet(Text(fsDict))

 

Please let me know what additional info may be helpful, if any. TIA!

0 Kudos
1 Solution

Accepted Solutions
NicoleJohnson
Regular Contributor

You can add an average score category field to your new dict and categorize it in your new featureset then use that for your chart instead of the uncategorized averages. I'm on mobile, but something like (in your new fs):

'averageScoreCat': When(averagescore > 4, "Score above 4", averagescore  < 4 && averagescore >= 3, "Score between 3 and 3.9", "No average score calculated")

I didn't type out all the category possibilities, you can just keep adding as you need.

Also, just as a side note because I'm curious, but a) what are you intending to do with the index variable? And b) were you having a problem getting the average without the Number function? 

View solution in original post

3 Replies
NicoleJohnson
Regular Contributor

You can add an average score category field to your new dict and categorize it in your new featureset then use that for your chart instead of the uncategorized averages. I'm on mobile, but something like (in your new fs):

'averageScoreCat': When(averagescore > 4, "Score above 4", averagescore  < 4 && averagescore >= 3, "Score between 3 and 3.9", "No average score calculated")

I didn't type out all the category possibilities, you can just keep adding as you need.

Also, just as a side note because I'm curious, but a) what are you intending to do with the index variable? And b) were you having a problem getting the average without the Number function? 

DanielleKuchler
Occasional Contributor

Thank you, Nicole! This was extremely helpful. I was previously trying to use an "IF" statement instead of the "When" statement. I was having trouble defining my dictionary item 'averagescore' as a variable, but found a workaround for the time being using the full average calculation... it's very clunky looking on my end, but it works exactly as desired!

DanielleKuchler_0-1722270579257.png

 

Here is what I ended up using to populate the 'averageScoreCat' field in my feature set:

//populate score category feature using averages    
   'averageScoreCat': When(
                (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 > 4, 'Score Above 4',
                (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 < 4 && (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 >= 3, 'Score between 3 and 3.9',
                (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 < 3 && (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 >= 2, 'Score between 2 and 2.9',
                (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 < 1 && (Number(feature['Leg_1_Ftg_Score']) + Number(feature['Leg_2_Ftg_Score']) + Number(feature['Leg_3_Ftg_Score']) + Number(feature['Leg_4_Ftg_Score']) + Number(feature['Mid_Body_Score']) + Number(feature['Upper_Body_Arms_Score']) + Number(feature['score']) + Number(feature['Coating_Cond_Score']) + Number(feature['groundingscore']) + Number(feature['cathodicprotection_score'])) / 10 >= 1, 'Score between 1 and 1.9',
                'No Average Score Calculated')

 

Also, to answer the questions:

a) I am not sure what the index variable does exactly, but I used this video as the inspiration (timestamp 6:20): https://youtu.be/rowW2PistiQ?si=MtRzalunD6frOLU6&t=382. The variable is referenced again in line 28 "[index++]". I still have a lot of learning to go when writing code, there is a possibility this was an unnecessary step 🙂

b) I was indeed having a problem getting the average without the Number function 😞 The data was pulled from Survey123 and all fields were automatically set up as String instead of Integer for this data. The Number function was the only way the calculation worked without getting an error message.

Thank you again for your help!!

0 Kudos
NicoleJohnson
Regular Contributor

Oh, that completely makes sense about the scores being strings (I just assumed they were already numbers and was confused why you'd need Number).

Thanks for the video! I'll have to experiment (and I've basically never touched Dashboards, so maybe there's something unique about Arcade expressions there, or the difference between writing an expression for a pop-up (what I'm used to doing) vs a chart).