thenineteen / Semiology-Visualisation-Tool

Data driven 3D brain visualisation of semiology. Semiology to anatomy translator based on over 4600 patients from 309 peer-reviewed articles.
MIT License
9 stars 6 forks source link

need global proportional lateralisation option #69

Closed thenineteen closed 3 years ago

thenineteen commented 4 years ago

currently, per individual study and patients, each semiology localisation gif parcellation is updated with a for loop in QUERY_LATERALISATION using the lateralising information.

There should be an option to force QUERY_LATERALISATION to map all the localisations first, then simply scale ALL OF THE GIFS using the few lateralising datapoints. This will also address the logging.debug the issue of:

some of the extracted lateralisation have no localisation - for now these are ignored but re-inspect!'
thenineteen commented 4 years ago

once I've done this for QUERY_LATERALISATION, I will let @fepegar know as we will need an updated API to allow for the setting to change as a checkbox: could call it "use global lateralising value".

ideally when this box is ticked it says "using global lateralising value" and if unticked (default), it says "using individual patient-level lateralising value"

fepegar commented 4 years ago

Is there anything to do for me atm? Seems like I need to wait until you implement this for QUERY_LATERALISATION.

thenineteen commented 4 years ago

nothing for you to do yet, as I haven't done this.

what's the best way for the API. I think it is best I write an entirely separate QUERY_LAT_GLOBAL module or function. Will that work for you?

thenineteen commented 4 years ago

probably related to pull return query statistics #108 and slicer log should not need to be opened #83

If we return the global lateralising values as proportions for each query and can display it as a lateralising bar on the coronals and axials, then this issue will also be resolved.

thenineteen commented 3 years ago

instead of another user tick box option as suggested above, it would be best to have the global lateralising proportion visible on all queries either as a percentage and/or color-bar as in #55

thenineteen commented 3 years ago

the display could show raw numbers in a table, similar to the GIF parcellation numbers, this is the aim:

image

how do we get there... using below pseudocode:

# output to show global lateralisation: note this section is already in semiology.py
# note that if the user doesn't determine a dominant hemisphere and doens't determine a latealising semiology (symptoms or sign)...
# ...then there is no lateralising R or L proportion. Whereas if we have all the info, we can just display e.g. 70% right.
# Otherwise, we only display the raw numbers/proportions: CL, IL, DomH and NonDomH

# as per query_lateralisation() in semiology module which calls:
all_combined_gifs, num_QL_lat, num_QL_CL, num_QL_IL, num_QL_BL, num_QL_DomH, num_QL_NonDomH = \
    QUERY_LATERALISATION(
        query_semiology_result,
        self.data_frame,
        map_df_dict,
        gif_lat_file,
        side_of_symptoms_signs=self.symptoms_side.value,
        pts_dominant_hemisphere_R_or_L=self.dominant_hemisphere.value,
    )

next we can use the returned values to make the above table.

In addition, if we know symptoms side or dominance, we can show the proportion lateralising to the left hemisphere:

Right = 0
Left = 0

# could skip the if statement but it's here to make the logic clear
if self.symptoms_side.value != Laterality.Neutral (alternatively is equal to Laterality.Right or Laterality.Left) OR self.dominant_hemisphere.value != Laterality.Neutral:

    # pt input
    if side_of_symptoms_signs == 'R':
        Right += num_QL_IL
        Left += num_QL_CL

    elif side_of_symptoms_signs == 'L':
        Right += num_QL_CL
        Left += num_QL_IL

    if pts_dominant_hemisphere_R_or_L:
        if pts_dominant_hemisphere_R_or_L == 'R':
            Right += num_QL_DomH
            Left += num_QL_NonDomH 
        elif pts_dominant_hemisphere_R_or_L == 'L':
            Right += num_QL_NonDomH 
            Left += num_QL_DomH

# now add extra info to the output table above or show as a horizontal bar indicator on axial/coronal slices:
If Right != 0 and Left != 0: 
    Left_Percentage = Left*100/(Left+Right)
    Right_Percentage = Right*100/(Left+Right)
    # preferably only show the larger one as both is redundant
fepegar commented 3 years ago

This will change the semiology API, which is used to connect mega_analysis and 3D Slicer. The changes will break some tests. I'll open a PR and then I can work on the Slicer stuff and you can remove the tests if you like (they should be used to test lower-level elements anyway).

fepegar commented 3 years ago

How would you compute the table from 2 semiologies?

thenineteen commented 3 years ago

This will change the semiology API, which is used to connect mega_analysis and 3D Slicer. The changes will break some tests. I'll open a PR and then I can work on the Slicer stuff and you can remove the tests if you like (they should be used to test lower-level elements anyway).

I was hoping we implement this as an feature on top of the current output, not instead of, so it doesn't change what we currently see in slicer table or 3D brain, and all tests still pass as its the same output as before, plus a global lateralising %?

thenineteen commented 3 years ago

How would you compute the table from 2 semiologies?

I'm going to answer this Q in my next comment below:

fepegar commented 3 years ago

As you have modified the output of QUERY_LATERALISATION, this is the only way I can see to propagate the lateralising data points information from the Semiology class to the output of the query.

fepegar commented 3 years ago

I think in most tests you could just replace heatmap = with heatmap, _ =.

thenineteen commented 3 years ago

... next we can use the returned values to make the above table.

In addition, if we know symptoms side or dominance, we can show the proportion lateralising to the left hemisphere:

Right = 0
Left = 0

# could skip the if statement but it's here to make the logic clear
if self.symptoms_side.value != Laterality.Neutral (alternatively is equal to Laterality.Right or Laterality.Left) OR self.dominant_hemisphere.value != Laterality.Neutral:

    # pt input
    if side_of_symptoms_signs == 'R':
        Right += num_QL_IL
        Left += num_QL_CL

    elif side_of_symptoms_signs == 'L':
        Right += num_QL_CL
        Left += num_QL_IL

    if pts_dominant_hemisphere_R_or_L:
        if pts_dominant_hemisphere_R_or_L == 'R':
            Right += num_QL_DomH
            Left += num_QL_NonDomH 
        elif pts_dominant_hemisphere_R_or_L == 'L':
            Right += num_QL_NonDomH 
            Left += num_QL_DomH

# now add extra info to the output table above or show as a horizontal bar indicator on axial/coronal slices:
If Right != 0 and Left != 0: 
    Left_Percentage = Left*100/(Left+Right)
    Right_Percentage = Right*100/(Left+Right)
    # preferably only show the larger one as both is redundant

So this was how to calculate the global lateralising proportions for a single semiology.

To do the same for n number of semiologies: S1, S2, ...Sn

There are two strategies which would be nice to have side by side as the second gives us a CI for combinations:

1. equal lateralising % weights: where i goes from 1 to n

Left_Percentage = np.mean([Left_Percentage(Si)])
Right_Percentage = 100 - Left_Percentage  # equivalent to Right_Percentage = np.mean(Right_Percentage (Si))

this would give an average lateralising percentage, which would help mitigate bias for less frequently occurring semiologies. Then to calculate confidence intervals we need a meta-analysis with inverse variance weighting

2. Pooling raw individual lateralising data with CI:


def pooling_raw_individual_lateralising_data():
    Left_Sum = np.sum([Left(Si)])
    Right_Sum = np.sum([Right(Si)])
    n = (Left_Sum +Right_Sum )
    Left_Proportion = Left_Sum / n

    Left_Percentage_Total = Left_Proportion * 100
    Confidence_Interval_95 = [
    100* (Left_Proportion - 1.96* np.sqrt( Left_Proportion * (1 - Left_Proportion ) / n )),
    100* (Left_Proportion + 1.96* np.sqrt( Left_Proportion * (1 - Left_Proportion ) / n )),
]

return Left_Percentage_Total, str(Confidence_Interval_95)
thenineteen commented 3 years ago

as a side note memo: Prof Williams recommended this for CI - will compare with python versions. also note at this point this is for both latealising and localising proportions, and not at the study level, but at the semiology level

https://statpages.info/confint.html