How to alter conditional statement for colouring of Scattermapbox based on two factors?

This week I started out with Python and Dash as I am eager to learn both.

My Goal:
I want to single out certain points which are detected by the sensors and color them red if they meet two conditions:

  • If they are detected between 01:00-05:00 a.m.
  • and if they are detected in a certain zone (assume that I have a list that has risk-probability per zone). So it should consider any risk larger than 0.6 also.

INTENDED CONDITION FOR COLORING:

IF x > 1 AND x < 5 AND risk[I] > 0.6 THEN color=red, ELSE listCoords.index.hour

Question:
How must the ‘color=np.append(np.insert(listCoords.index.hour, 0, 0), 23)’ part of my code change to reach my goal? I was thinking something along the lines of (without including the risk-list yet):

color=np.where(np.logical_and(listCoords.index.hour >= 1, listCoords.index.hour <= 5), np.append(np.insert(listCoords.index.hour, 0, 0), 23), 'red'),

But this does not work. Any help is much appreciated. :slight_smile:

Snippet of my code: scatter of points on geo map:

return go.Figure(
    data=[
        # Data for all rides based on date and time
        Scattermapbox(
            lat=listCoords["Lat"],
            lon=listCoords["Lon"],
            mode="markers",
            hoverinfo="text + lat + lon",
            text=listCoords.index.hour,
            marker=dict(
                showscale=True,  
                color=np.append(np.insert(listCoords.index.hour, 0, 0), 23),
                opacity=0.5,
                size=5,
                colorscale=[
                    [0, "#F4EC15"],
                    [0.04167, "#DAF017"],
                    [0.0833, "#BBEC19"],
                    [0.125, "#9DE81B"],
                    [0.1667, "#80E41D"],
                    [0.2083, "#66E01F"],
                    [0.25, "#4CDC20"],
                    [0.292, "#34D822"],
                    [0.333, "#24D249"],
                    [0.375, "#25D042"],
                    [0.4167, "#26CC58"],
                    [0.4583, "#28C86D"],
                    [0.50, "#29C481"],
                    [0.54167, "#2AC093"],
                    [0.5833, "#2BBCA4"],
                    [1.0, "#613099"],
                ],
                colorbar=dict(
                    title="Time of<br>Day",
                    x=0.93,
                    xpad=0,
                    nticks=24,
                    tickfont=dict(color="#d8d8d8"),
                    titlefont=dict(color="#d8d8d8"),
                    thicknessmode="pixels",
                ),
            ),
        ),

EDIT 1: providing sample data so people can run it:

List of some sensor detections with their date/time stamp:

**Date/Time	         Lat 	             Lon**
2019-03-25 00:05:00	-10,80948998827914	24,19160777427344  
2019-03-25 00:10:00	-10,79868405083584	24,16288145431259
2019-03-25 04:05:00	-10,78688335083584	24,20288145431259
2019-03-25 04:05:00	-10,77558405083584	24,288145431259

List of zones and their risk probability:

ZoneRisk_Prob          Zone
0	                    1
0	                    2
0,002394936420140275	3
0,030364372469635626	4
0,00005702229571762559	5
0	                    6
0,039345384045161656	7
0,10164224211666761	    8
0,14854308034441466	    9
0,0037064492216456633	10
0	                    11
0	                    12
0,0003421337743057536	13
0,1214289787306837	    14
0,04410674573758339	    15
0	                    16
0	                    17
0,0158236870616411	    18
0	                    19
0,18951359981752866	    20
0,0014825796886582653	21
0,0005417118093174431	22
0,027769858014483662	23
0,014027484746535895	24
0,0012259793579289502	25
0,029737127216741745	26
0,009636767976278725	27
0,060072988538518564	28
0,043051833266807324	29
0,005759251867480185	30
0,1094257854821235	    31

EDIT:

  • First I categorized/converted all the risk_probabilities into ‘1’, ‘2’ and ‘3’. Where 3 is highest risk.
  • Then I, Synchronized Dataset (basically bringing both datesets into alignment into one file)

new dataset threat_points.csv:

Date/Time                     Lat,                              Lon,                              Threat
2019-03-23 04:00:00,   -14.809489988279145,  26.191607774273443,    1
2019-03-23 04:00:00,   -14.792921094981814,  26.191716715339687,    2
2019-03-23 04:05:00,   -14.798684405083584,  26.162881454312586,    3
2019-03-23 04:10:00,   -14.80112670820822,    26.173830400821103,    3

Then based on the code-pointers from @empet, I set the following items:

# Initialize data frame
df1 = pd.read_csv(
     "/Users/ME/Desktop/DSP_Frontend/threat_points.csv",
    dtype=object,
)
df = pd.concat([df1], axis=0)
df["Date/Time"] = pd.to_datetime(df["Date/Time"], format="%Y-%m-%d %H:%M")
df.index = df["Date/Time"]
df.drop("Date/Time", 1, inplace=True)

#########################################################
##################### FACTOR 1 ##########################
#########################################################

# Take from df1 file the values of column 'Threat'
probs = df1['Threat'].values

# Convert the string values to integer in new list
probs1 = np.asarray(probs, dtype = int)

#  Filter only the high priority items which are equal to 3.
r = np.where(probs1 == 3)[0]

# Print check the possible locations of poachers
print("Factor 1 (risk probability) = SUCCESS")

#########################################################
##################### FACTOR 2 ##########################
#########################################################

##extract the list of datetimes between two fixed hours:#
time_red = df.between_time('01:00', '05:00').index

# # Print check the times
# print("At these timestamps: (without rows_nr) ",time_red)
print("Factor 2 (DateTime Range) = SUCCESS")

#get the row number in df.index of each element in time_red
row_nr = [list(df.index).index(tr) for tr in time_red]

# #extract the common elements in r and row_nr:
red_position = set(r).intersection(set(row_nr))
print("Factor 3 (Find Intersection between F1 and F2) = SUCCESS")

print(red_position)

This outputs row numbers:
{15268, 15422, 24713, 14667, 21080, 11928, 33136, 35219, 35220, 17716, 35222, 35223, 35224, 14681, 35226, 35227, 35228, 158}

Now coming to the conditional styling, this is what I have in mind:

return go.Figure(
        data=[
            # Data for all rides based on date and time
            Scattermapbox(
                lat=listCoords["Lat"],
                lon=listCoords["Lon"],
                mode="markers",
                hoverinfo="text + lat + lon",
                text=listCoords.index.hour,
                marker=dict(
                    showscale=True,
                    color=np.where(np.logical_if(red_position, else), 'red', np.append(np.insert(listCoords.index.hour, 0, 0), 23)),
                    opacity=0.5,
                    size=5,
                    colorscale=[
                        [0, "#F4EC15"],
                        [0.04167, "#DAF017"],
                        [0.0833, "#BBEC19"],
                        [0.125, "#9DE81B"],
                        [0.1667, "#80E41D"],
                        [0.2083, "#66E01F"],
                        [0.25, "#4CDC20"],
                        [0.292, "#34D822"],
                        [0.333, "#24D249"],
                        [0.375, "#25D042"],
                        [0.4167, "#26CC58"],
                        [0.4583, "#28C86D"],
                        [0.50, "#29C481"],
                        [0.54167, "#2AC093"],
                        [0.5833, "#2BBCA4"],
                        [1.0, "#613099"],
                    ],
                    colorbar=dict(
                        title="Time of<br>Day",
                        x=0.93,
                        xpad=0,
                        nticks=24,
                        tickfont=dict(color="#d8d8d8"),
                        titlefont=dict(color="#d8d8d8"),
                        thicknessmode="pixels",
                    ),
                ),
            ),

Hi @mirceau,

Welcome to Plotly forum!

To get the positions that meet both your requirements you should proceed as below.

Read the two data files, that are supposed to be syncronized, in the sense that
datetime recorded in the row k of the datetime file, corresponds to the location of coords (lon, lat) in the row number k of the other file.

dfz = pd.read_csv("zone-file.csv")
probs = dfz['ZoneRisk_Prob'].values
I = np.where(probs > 0.6)[0]
print(I)
dft = pd.read_csv("record-datetime.csv") #supose that this dataframe has Date/Time as index

#extract the list of datetimes between two fixed hours:

time_red = dft.between_time('01:00', '05:00').index
print(time_red)

#get the row number in dft.index of each element in  time_red

row_nr = [list(df.index).index(tr) for tr in time_red]
print(row_nr)

#extract the common elements in I and row_nr:

red_position = set(I).intersection(set(row_nr))

If, for example, red_position =[ 7,12, 17, 25],
then color[7], color[12], color[17], color[25] will be red

2 Likes

Thanks for the help @empet,

Perhaps it would be simpler for me if I merge the two files first indeed so I can get that synchronisation you talked about. Then I can just read one CSV and continue as follow.

And as I understand your last comment, my eventual condition-statement will look something like:

IF red_position THEN 'color':'red' ELSE np.append(np.insert(listCoords.index.hour, 0, 0), 23)

Is this the correct understanding? I will try to apply the code as soon as I am home and let you know if I need any other pointers into the right direction. :slight_smile:

@mirceau

To give an answer I have to know what represent the numerical values stored in the list color, that you intend to map to a colorscale. More precisely from your code it results that you are assigning a color to each marker, according to the corresponding value in color. Depending on how you defined the list color, I can suggest how to modify your colorscale to ensure the red color for the markers corresponding to red_positions.

1 Like

@empet, Please see post EDIT.

So, yes, the value by which currently the color-assignment is performed is: time-of-day. So the scatterplots currently are either blue-ish if their timestamp is during the night and green/yellow-ish if their timestamp is during the day.

Perhaps, since everything is in one file now, all synchronised… there is a way to check for both conditions in one line… so GET the rows of those records where timestamp is between 1 and 5, and are of priority ‘3’. And put the obtained values (lat,lon) into red_positions. (I currently have the general lat,lon/timestamps in listCoords.index.hour)

So perhaps in the below conditional-styling line I should say something along the lines of:

> for the color red those records in listCoords.index.hour that correspond with red_positions.

color=np.where(np.logical_if(red_position), 'red', np.append(np.insert(listCoords.index.hour, 0, 0), 23)),

Any pointers on how to do this?

@mirceau

If you have 3 categories of data that will be colored blue, green and red, then it is recommended to define
marker_color as a list of color names/codes, and do not set marker_colorscale, i.e.

marker_color = ['red', 'blue', 'blue', 'red', 'green', 'blue', 'red']

(you can replace the color name by its color code.

If you use a continuous marker_colorscale (a colorscale as that you defined in your code), and marker_color= [3, 1, 1, 3, 2, 1, 3], then plotly will normalize
the color values, i.e. map them to the interval [0, 1] by (val-vmin)/(vmax-vmin) and then to to each normalized value is assigned the corresponding color code in the colorscale (eventually by interpolation), and you cannot be sure that you get the desired color.

The third possibility is to define a discrete colorscale.

But since you have only three categories, the first method of defining `marker_color’ is recommended

red_position is a list of row numbers, as I have defined in the previous post, and I do not understand
what do you mean by np.logical_if(red_position)

I’m not aware of the existence of a function np.logical_if. I know np.logical_and, np.logical_or, etc (see https://docs.scipy.org/doc/numpy/reference/generated/numpy.logical_and.html).

1 Like