Hi,
I'm reading hosted feature layers from AGOL into a spatially enabled dataframe.
The fields that have coded value domains on them pass through the code from the domain, not the description.
Is there any way to substitute the code with the description when creating the dataframe? I can't find anything about that in the documentation.
Thanks
Hanlie
Solved! Go to Solution.
You can't sub in the descriptions initially, but a simple replacement is quite easy. I have a little function I use in scripts where I want the descriptions instead of the codes.
def domain_descriptions(df, service):
"""
Takes a queried dataframe and its parent service and applies all fieldname aliases and domain names.
Not especially useful for updating web layers, but *great* for when the queried dataframe will be exported to some other format.
Parameters:
df (DataFrame): A dataframe queried out of a hosted feature service or table.
service (FeatureLayer or Table): A layer or table from a Feature Service.
Returns:
None, just modifies the input DataFrame.
"""
aliases = {f'{i["name"]}':f'{i["alias"]}' for (i) in service.properties['fields']}
domains = {i['name']:{str(j['code']):j['name'] for (j) in i['domain']['codedValues']} for (i) in service.properties["fields"] if 'domain' in i and i['domain'] is not None}
for d in domains:
if d in df.columns.to_list():
df[d] = df[d].astype('str').replace(domains[d])
df.rename(columns=aliases, inplace=True)
Try it out! In practice, it might look like this:
fl = gis.content.get('some-itemid').layers[0]
df = fl.query(as_df=True)
domain_descriptions(df, fl)
You can't sub in the descriptions initially, but a simple replacement is quite easy. I have a little function I use in scripts where I want the descriptions instead of the codes.
def domain_descriptions(df, service):
"""
Takes a queried dataframe and its parent service and applies all fieldname aliases and domain names.
Not especially useful for updating web layers, but *great* for when the queried dataframe will be exported to some other format.
Parameters:
df (DataFrame): A dataframe queried out of a hosted feature service or table.
service (FeatureLayer or Table): A layer or table from a Feature Service.
Returns:
None, just modifies the input DataFrame.
"""
aliases = {f'{i["name"]}':f'{i["alias"]}' for (i) in service.properties['fields']}
domains = {i['name']:{str(j['code']):j['name'] for (j) in i['domain']['codedValues']} for (i) in service.properties["fields"] if 'domain' in i and i['domain'] is not None}
for d in domains:
if d in df.columns.to_list():
df[d] = df[d].astype('str').replace(domains[d])
df.rename(columns=aliases, inplace=True)
Try it out! In practice, it might look like this:
fl = gis.content.get('some-itemid').layers[0]
df = fl.query(as_df=True)
domain_descriptions(df, fl)
Thanks very much Josh, your script does exactly what I needed. 🙂
This is excellent. I was coding something very similar. Just adding to the thread... I also have range domains in my data so had to ignore these by just looking at the coded domains.
coded_domains = {fld.name:{str(entry.code):entry.name for entry in fld.domain.codedValues} for fld in lyr.properties.fields if fld.domain and fld.domain.type == "codedValue"}