VIP
  • Reports
  • Data Catalogue
  • Weekly Analysis
  • Frontier

On this page

  • Map

Hair Sampling Map

Published

September 29, 2025

Abstract

Simple map of communties with hair sample collection status

Code
```{python}
# load in list of people who did the hair sample
HAIR_SAMPLE = "/Users/st2246/Work/Pilot3/data/raw/main/09. Hair Cortisol/hair_sampled list from TRL.dta"
hair_sample_list = pd.read_stata(HAIR_SAMPLE, convert_categoricals=False)
hair_sample_list["hh_id"] = hair_sample_list["compid"].astype("int64")
# As int
hair_sample_list["sample_collected"] = (
    hair_sample_list["sample_status"] == "YES"
).astype("int32")
hair_sample_list["sample_refused"] = (hair_sample_list["sample_status"] == "NO").astype(
    "int32"
)
hair_sample_list["sample_NA"] = (hair_sample_list["sample_status"] == "N.A").astype(
    "int32"
)
```
Code
```{python}
# Aggregate to community level
df = (
    hair_sample_list.groupby(["community_id"])
    .agg(
        num_hh=("hh_id", "nunique"),
        sample_collected=("sample_collected", "sum"),
        sample_refused=("sample_refused", "sum"),
        sample_NA=("sample_NA", "sum"),
    )
    .reset_index()
)
```
Code
```{python}
communities = communities.merge(df, on=["community_id"], how="left", validate="1:1")
```

Map

Below is an interactive map that plots hair sample collection progress for all communities.

Note

Note: highlighted communities have at least 5 households whose hair sample is missing (not collected & not refused). The size of the circle is proportional to the number of households whose hair sample is missing. The color indicates the district.

Hover over a community to see the exact counts.

Code
```{python}
import folium
from folium.plugins import Draw


# Create a map centered on a location
m = folium.Map(
    location=[communities["centroid_lat"].mean(), communities["centroid_lon"].mean()],
    zoom_start=9,
)

m2 = folium.Map(
    location=[communities["centroid_lat"].mean(), communities["centroid_lon"].mean()],
    zoom_start=9,
)


# 3. Define a simple function to assign a color based on the 'category' column
def get_color(row, desaturate: bool):
    district = row["district"]
    if district.lower() == "karaga":
        color = "lightblue" if desaturate else "darkblue"
    elif district.lower() == "mion":
        color = "lightgreen" if desaturate else "darkgreen"
    elif district.lower() == "tolon":
        # light gray hex
        color = "gray" if desaturate else "black"
    else:
        color = "pink" if desaturate else "red"
    return color


# 4. Loop through each row in the DataFrame to add a CircleMarker
for idx, row in communities.iterrows():
    radius = row["sample_NA"] * 40
    info_text = f"""
            Community: {row["community"]}<br>
            Estimated Cpds: {row["estimated_compounds"]}<br>
            Households: {row["num_hh"]}<br>
            Sample Collected: {row["sample_collected"]}<br>
            Sample Refused: {row["sample_refused"]} ({row["sample_refused"] /(row["sample_refused"] + row["sample_collected"]):.1%} of attempts refused) <br>
            Sample Missing: {row["sample_NA"]} ({row["sample_NA"] / row["num_hh"]:.1%})
            """
    desaturate = row["sample_NA"] < 5
    fill = get_color(row, desaturate)
    border = get_color(row, desaturate)
    # Add the CircleMarker to the map
    circle = folium.Circle(
        location=[row["centroid_lat"], row["centroid_lon"]],
        radius=radius,
        color=border,
        fill=True,
        fill_color=fill,
        fill_opacity=1,
        popup=info_text,
        tooltip=info_text,
    )
    circle2 = folium.Circle(
        location=[row["centroid_lat"], row["centroid_lon"]],
        radius=radius,
        color=border,
        fill=False,
        popup=info_text,
        tooltip=info_text,
    )
    if (
        row["sample_NA"] > 0
        and row["sample_refused"] / (row["sample_refused"] + row["sample_collected"])
        < 0.5
    ):
        circle.add_to(m)
    elif (
        row["sample_NA"] > 0
        and row["sample_refused"] / (row["sample_refused"] + row["sample_collected"])
        >= 0.35
    ):
        circle2.add_to(m)

```
Note

Map only includes communities with at least one household whose hair sample is missing. If everyone in the communitie has had their hair sample collected OR refused their collection, then they are ommitted from the map.

Code
```{python}
m
```
Make this Notebook Trusted to load map: File -> Trust Notebook
Code
```{python}
from itables import show

show(communities)
```
Loading ITables v2.5.2 from the internet... (need help?)
 
Cookie Preferences