Skip to content

Commit

Permalink
- Got rid of precision/recall in cset data tables because people found
Browse files Browse the repository at this point in the history
  them confusing and AMIA reviewers complained that I was using the word
  wrong.
- Added column telling what vocabularies the concepts come from.
- Cleaned up min_col, which wasn't being used.
- More clean up of editAction stuff
  • Loading branch information
Sigfried committed Nov 21, 2024
1 parent c7fa0ca commit aedb93c
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 171 deletions.
23 changes: 18 additions & 5 deletions backend/routes/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,21 +308,34 @@ def next_api_call_group_id() -> Optional[int]:

@router.post("/related-cset-concept-counts")
def get_related_cset_concept_counts(concept_ids: List[int] = None, verbose=True) -> Dict:
"""Returns dict of codeset_id: count of included concepts"""
"""Returns dict of codeset_id: count of included concepts
2024-11-21: adding counts by vocab
"""
query = f"""
SELECT DISTINCT codeset_id, concept_id
FROM concept_set_members
SELECT codeset_id, vocabulary_id, COUNT(DISTINCT concept_id) cnt
FROM cset_members_items
WHERE concept_id = ANY(:concept_ids)
AND csm
GROUP BY 1, 2
"""
with get_db_connection() as con:
csm = sql_query(con, query, {'concept_ids': concept_ids}, )

counts = {}
for record in csm:
codeset_id = record['codeset_id']
counts[codeset_id] = counts.get(codeset_id, 0) + 1
counts[codeset_id] = counts.get(codeset_id, 0) + record['cnt']

return counts
vcounts = {}
for record in csm:
codeset_id = int(record['codeset_id'])
vcounts[codeset_id] = vcounts.get(codeset_id, {})
cnt = counts.get(codeset_id)
pct = record['cnt'] / cnt
vcounts[codeset_id]['concepts'] = cnt
vcounts[codeset_id][record['vocabulary_id']] = pct

return vcounts


@router.get("/get-all-csets")
Expand Down
22 changes: 0 additions & 22 deletions frontend/src/components/CsetComparisonPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {Tooltip} from './Tooltip';
import {
cellContents,
cellStyle,
getCodesetEditActionFunc,
Legend,
newCsetAtlasWidget,
textCellForItem,
Expand Down Expand Up @@ -455,15 +454,6 @@ export function CsetComparisonPage() {
>
Create new concept set or version
</Button>,

/* <FlexibleContainer key="cset-table" title="Table of concept set being edited"
position={panelPosition} countRef={countRef}>
<CsetsDataTable show_selected={true}
min_col={false}
clickable={false}
showTitle={false}
selected_csets={selected_csets} />
</FlexibleContainer>, */
];

let edited_cset;
Expand All @@ -489,11 +479,6 @@ export function CsetComparisonPage() {
/>
</FlexibleContainer>,
);
/* infoPanels.push(
<FlexibleContainer key="compare" title={edited_cset.concept_set_name}>
<CsetsDataTable {...props} show_selected={true} min_col={false} />
</FlexibleContainer>
); */

infoPanels.push(
<FlexibleContainer key="instructions"
Expand Down Expand Up @@ -647,10 +632,6 @@ function StatsAndOptions(props) {
);
}

function precisionRecall(props) {
// TODO: write someday -- precision recall of newCset against all? Or each against all? Not sure what the plan was
}

function nodeToTree(node) { // Not using
// a flat tree
const subTrees = node.childIds().map(n => nodeToTree(n));
Expand Down Expand Up @@ -809,8 +790,6 @@ function getColDefs(props) {
} = props;
const {nested, } = graphOptions;

const editAction = getCodesetEditActionFunc({csmi, newCset, newCsetDispatch});

let coldefs = [
{
name: 'Concept name',
Expand Down Expand Up @@ -1119,7 +1098,6 @@ function getColDefs(props) {
...props,
row,
cset_col,
editAction,
});
},
conditionalCellStyles: [
Expand Down
43 changes: 17 additions & 26 deletions frontend/src/components/Csets.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import React, {
useState,
useRef,
useEffect,
useCallback, /* useReducer, */
} from 'react';
import axios from 'axios';
import React, { useState, useEffect, } from 'react';
import { CsetsDataTable } from './CsetsDataTable';
// import {difference, symmetricDifference} from "./utils";
import {pct_fmt} from "../utils";
import ConceptSetCards from './ConceptSetCard';
import Alert from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle';
Expand All @@ -17,20 +11,12 @@ import {
createFilterOptions,
Chip,
} from '@mui/material';
import { matchSorter } from 'match-sorter';
import Button from '@mui/material/Button';
// import Chip from '@mui/material/Chip';
import { every, keyBy, union, uniq, orderBy, difference } from 'lodash';
import { get, isNumber, isEmpty, flatten, intersection } from 'lodash';
// import {isEqual, pick, uniqWith, max, omit, uniq, } from 'lodash';
// import Box from "@mui/material/Box";
import { Tooltip } from './Tooltip';
// import { matchSorter } from 'match-sorter';
import { get, keyBy, orderBy, difference, isNumber, isEmpty, flatten } from 'lodash';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
// import * as po from '../pages/Popover';
import { DOCS } from './AboutPage';
import { useDataCache } from '../state/DataCache';
import { useDataGetter, getResearcherIdsFromCsets } from '../state/DataGetter';
import {useCids, useCodesetIds} from '../state/AppState';

Expand Down Expand Up @@ -291,12 +277,18 @@ export function ConceptSetsPage () {
let _allRelatedCsetsArray = relatedCodesetIds.map(
codeset_id => {
let cset = selected_csets[codeset_id] || {...allCsetsObj[codeset_id]};
cset['intersecting_concepts'] = relatedCsetConceptCounts[codeset_id];
cset['recall'] = cset['intersecting_concepts'] / concept_ids.length;
cset['precision'] = cset['intersecting_concepts'] / cset.counts.Members;
if (isNaN(cset['recall']) || isNaN(cset['precision'])) {
console.warn(`WHY ARE recall or precision NaN?`);
}
let counts = Object.entries(relatedCsetConceptCounts[codeset_id]);
counts = orderBy(counts, d => d[0]);
let vocabs = [];
counts.forEach(([key, val]) => {
if (key === 'concepts') {
cset['intersecting_concepts'] = val;
} else {
val = Number(val).toLocaleString(undefined, {style:'percent', maximumFractionDigits: 2});
vocabs.push(`${val}\u00A0${key}`);
}
});
cset['vocabs'] = vocabs.join(', ');
return cset;
});

Expand All @@ -319,8 +311,7 @@ export function ConceptSetsPage () {

let relatedCsets = Object.values(allRelatedCsets).
filter(cset => !cset.selected);
relatedCsets = orderBy(relatedCsets, ['selected', 'precision'],
['desc', 'desc']);
relatedCsets = orderBy(relatedCsets, ['selected', 'intersecting_concepts'], ['desc', 'desc']);
// This is here for making screenshots for paper. TODO: Might want to make a user control
// for this.
// relatedCsets = relatedCsets.filter(d => d.precision < 1 || (d.counts||{}).Members > 10);
Expand Down
68 changes: 17 additions & 51 deletions frontend/src/components/CsetsDataTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ export function CsetsDataTable(props) {
const relatedCsets = show_selected ? null : props.relatedCsets;
const all_csets = show_selected ? null : props.all_csets;
const concept_ids = show_selected ? null : props.concept_ids;
const min_col = show_selected ?
("min_col" in props ? props.min_col : true) : false;

let coldefs = getColdefs(min_col);
let coldefs = getColdefs();
/* const conditionalRowStyles = [{ when: row => row.selected,
style: { backgroundColor: 'rgba(63, 195, 128, 0.9)', color: 'white',
'&:hover': { cursor: 'pointer', }, } }]; */
Expand Down Expand Up @@ -136,13 +134,13 @@ export function CsetsDataTable(props) {
);
}

function getColdefs(min_col = false) {
function getColdefs() {
/*
const descending = (rows, selector, direction) => {
return orderBy(rows, selector, ['desc']);
};
*/
let coldefs_first_4 = [
let coldefs = [
// { name: 'level', selector: row => row.level, },
{
name: "Version ID",
Expand Down Expand Up @@ -183,23 +181,26 @@ function getColdefs(min_col = false) {
center: true,
sortable: true,
},
];
let coldefs_last_4 = [
{
// name: 'Recall',
name: (
<Tooltip label="Portion of concepts in the selected concept sets that belong to this set.">
<span>Recall</span>
</Tooltip>
<Tooltip label="Number of concepts in this set overlapping with all the concepts selected.">
<span>Common</span>
</Tooltip>
),
selector: (row) => row.recall,
format: (row) => pct_fmt(row.recall),
desc: true,
selector: (row) => row.intersecting_concepts,
compact: true,
width: "70px",
width: "66px",
center: true,
sortable: true,
},
{
name: 'Vocabularies',
selector: (row) => row.vocabs,
compact: true,
width: "200px",
sortable: true,
wrap: true,
},
{
name: (
<Tooltip label="Approximate distinct person count. Small counts rounded up to 20.">
Expand Down Expand Up @@ -244,42 +245,7 @@ function getColdefs(min_col = false) {
*/
];

if (!min_col) {
let coldefs_extra = [
{
// name: 'Shared concepts',
name: (
<Tooltip label="Number of concepts in this set that also belong to the selected concept sets.">
<span>Shared</span>
</Tooltip>
),
selector: (row) => row.intersecting_concepts,
compact: true,
width: "70px",
center: true,
sortable: true,
},
{
name: (
<Tooltip label="Portion of the concepts in this set shared with the selected concept sets.">
<span>Precision</span>
</Tooltip>
),
selector: (row) => row.precision,
format: (row) => pct_fmt(row.precision),
desc: true,
compact: true,
width: "70px",
center: true,
sortable: true,
// sortFunction: descending,
},
];

return [...coldefs_first_4, ...coldefs_extra, ...coldefs_last_4];
}

return [...coldefs_first_4, ...coldefs_last_4];
return coldefs;
}

function getCustomStyles() {
Expand Down
63 changes: 2 additions & 61 deletions frontend/src/components/NewCset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,60 +68,6 @@ export function expandCset({ newCset, graphContainer, }) {
return expansion;
}

export function getCodesetEditActionFunc({ newCset, newCsetDispatch, csmi }) {
return (props) => {
// this function will be called editAction and passed around as needed
const {
evt,
// csmi, // not sure if this should come from closure or props sent to the generated function
clickAction,
flag,
cset_col: { codeset_id },
row: { concept_id },
no_action = false,
} = props;


let definition = get(csmi, [codeset_id, concept_id]);
if (clickAction) {
definition = { ...definition };
if (clickAction.startsWith("Cancel")) {
newCsetDispatch({type: 'deleteDefinition', concept_id});
return;
}
if (isEmpty(definition)) {
if (clickAction === "Add") {
definition = { codeset_id, concept_id, csm: false, item: true };
Object.keys(FLAGS).forEach((flag) => {
definition[flag] = false;
});
} else {
throw new Error("wasn't expecting no item except on Add");
}
} else {
if (clickAction === "Add") {
definition.item = true;
Object.keys(FLAGS).forEach((flag) => {
definition[flag] = false;
});
}
}
}


if (clickAction === "Update") {
definition[flag] = !definition[flag];
}
if (clickAction.startsWith("Cancel")) {
// moved this up above to dispatch action and return
} else {
// csidState[concept_id] = definition;
newCsetDispatch({type: 'addDefinition', definition});
}
evt.stopPropagation();
};
}

const FLAGS = {
// includeMapped: {component: TranslateIcon, sz:12, tt: 'Include Mapped'},
// includeDescendants: {component: TreeIcon, sz:12, tt: 'Include descendants'},
Expand Down Expand Up @@ -446,7 +392,7 @@ export function cellContents(props) {
- If staged for deletion:
- Just the word 'Deleted', clicking cancels deletion
*/
const { editAction, newCset, newCsetDispatch } = props;
const { newCset, newCsetDispatch } = props;
const { item, editing, codeset_id, concept_id } = cellInfo(props);
let removeIcon, clickAction, contents;
let flags = Object.keys(FLAGS);
Expand Down Expand Up @@ -483,8 +429,6 @@ export function cellContents(props) {
definition[flag] = false;
});
newCsetDispatch({type: 'addDefinition', definition});

//editAction({ evt, ...props, clickAction })}
}}
/>
);
Expand All @@ -494,7 +438,6 @@ export function cellContents(props) {
<Tooltip label={clickAction}>
<BlockIcon
onClick={(evt) => newCsetDispatch({type: 'deleteDefinition', concept_id: item.concept_id})}
// editAction({ evt, ...props, item, clickAction })}
sx={{
width: "12px",
height: "12px",
Expand All @@ -518,9 +461,7 @@ export function cellContents(props) {
contents = <span>{checkmark}{contents}</span>;
}
let cellStuff = (
<div //onClick={(evt) => { editAction({ evt, ...props, clickAction, no_action: true });}}
style={{display: "flex", alignItems: "center", gap: "4px", marginTop: "1px"}}
>
<div style={{display: "flex", alignItems: "center", gap: "4px", marginTop: "1px"}} >
{contents}
</div>
);
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/state/AppState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,6 @@ const newCsetReducer = (state, action) => {
// "codeset_created_by": "e64b8f7b-7af8-4b44-a570-557b812c0eeb", // will be set by enclave
is_draft: true,
researchers: [],
/*
counts: {'Expression items': 0},
intersecting_concepts: 0,
precision: 0,
recall: 0,
*/
};
/*
if (state.currentUserId) {
Expand Down
Loading

0 comments on commit aedb93c

Please sign in to comment.