-2
$\begingroup$

I have a json file of protein data of mouse and human like

{
    "name": "P04202",
    "full_name": "Transforming growth factor beta-1 proprotein",
    "symbol": "TGFB1_MOUSE",
    "length": 390,
    "species": "mouse",
    "function": "Transforming growth factor beta-1 proprotein: Precursor of the Latency-associated peptide (LAP) and Transforming growth factor beta-1 (TGF-beta-1) chains, which constitute the regulatory and active subunit of TGF-beta-1, respectively.",
    "nr_modifications": 2821,
    "residues": [
        {
            "pos": 1,
            "residue": "M",
            "modifications": []
        },
        {
            "pos": 2,
            "residue": "P",
            "modifications": []
        },
        ...
        {
            "pos": 60,
            "residue": "A",
            "modifications": [
                {
                    "mod": "[127] Fluoro",
                    "type": "Chemical derivative",
                    "prob": 1
                },
                {
                    "mod": "[53] HNE",
                    "type": "Post-translational",
                    "prob": 0.25
                }
            ]
        },
        ...
        ]
}

How do I create a scatter plot [![enter image description here][1]][1]

Thanks for any idea

So far I gathered a code to fetch the data from an online json

export const load = ({ fetch }) => {
  const fetchProteins = async () => {
    const res = await fetch('https:address/assets/ptm.json')
    const data = await res.json()
    return data
  }

  return {
    Proteins: fetchProteins()
  }
}

This code generates a scatter plot but I will need to hover around the dots

script>
    import { extent } from 'd3-array';
    import { scaleLinear } from 'd3-scale';
    export let data = [];

    const margin = 30;

    $: xScale = scaleLinear()
        .domain(extent(data.proteins.map((d) => d.length)))
        .range([0, 800 - (margin * 2)]);
    
    $: yScale = scaleLinear()
        .domain(extent(data.proteins.map((d) => d.nr_modifications)))
        .range([0, 400 - (margin * 2)]);
</script>
  
  
<svg width="800" height="400">
    <g transform="translate({margin}, {margin})">
        {#each data.proteins as datapoint}
            <circle
                cx={xScale(datapoint.length)}
                cy={yScale(datapoint.nr_modifications)}
                class:human={datapoint.species === 'human'}
                r="20"
            />
        {/each}
        <circle
                cx={10}
                cy={10}
                class="human"
                r="10"
            />
            <circle
            cx={10}
            cy={40}
            class="mouse"
            r="10"
        />
          
    </g>
</svg>

<style>
    svg {
        border: 1px;
        border-style: solid;
    }
    circle {
        fill: steelblue;
        fill-opacity: 0.5;
    }
    .human {
        fill: red;
    }
</style>
$\endgroup$
4
  • $\begingroup$ What did you try so far? $\endgroup$
    – user438383
    Commented May 11, 2023 at 14:58
  • $\begingroup$ I gathered a code to fetch the data from an online json $\endgroup$
    – Zizogolu
    Commented May 11, 2023 at 15:03
  • $\begingroup$ "fetch data from online JSON" is basically downloading a file and maybe subsetting it. What else have you tried in actually generating the plot? $\endgroup$
    – Ram RS
    Commented May 11, 2023 at 16:57
  • $\begingroup$ Yesterday I accomplished to plot a scatter chart but I will need to deploy hover around the dots $\endgroup$
    – Zizogolu
    Commented May 12, 2023 at 8:07

1 Answer 1

3
$\begingroup$

It seems like you are already on the right track. The code you provided fetches the protein data from the JSON file and generates a scatter plot with D3.js based on two properties: length and nr_modifications.

To add an interactive hover event, you can add a tooltip to your scatterplot. The tooltip will display additional information when you hover over each dot.

Below is an example of how you can add a tooltip:

First, add a <div> element in your HTML that will be used as the tooltip:

<div id="tooltip" style="position: absolute; opacity: 0;">
    <p><strong>Protein:</strong> <span id="protein"></span></p>
    <p><strong>Modifications:</strong> <span id="modifications"></span></p>
    <p><strong>Length:</strong> <span id="length"></span></p>
</div>

Then, in your script, add the code to handle the mouseover and mouseout events for each circle:

{#each data.proteins as datapoint}
    <circle
        cx={xScale(datapoint.length)}
        cy={yScale(datapoint.nr_modifications)}
        class:human={datapoint.species === 'human'}
        r="20"
        on:mouseover="{(e) => handleMouseover(e, datapoint)}"
        on:mouseout="{(e) => handleMouseout(e)}"
    />
{/each}

Define the handleMouseover and handleMouseout functions in your script. These functions will show and hide the tooltip respectively. For the handleMouseover function, it also updates the tooltip content with the protein data:

<script>
    // ... your existing code ...

    const handleMouseover = (event, datapoint) => {
        // Update the tooltip content
        document.getElementById('protein').textContent = datapoint.symbol;
        document.getElementById('modifications').textContent = datapoint.nr_modifications;
        document.getElementById('length').textContent = datapoint.length;

        // Show the tooltip
        document.getElementById('tooltip').style.opacity = 1;

        // Position the tooltip
        document.getElementById('tooltip').style.left = event.pageX + 'px';
        document.getElementById('tooltip').style.top = event.pageY + 'px';
    };

    const handleMouseout = () => {
        // Hide the tooltip
        document.getElementById('tooltip').style.opacity = 0;
    };
</script>

In standard HTML or JavaScript, the {#each} syntax and usage of {} for expressions and variable binding won't work as they are specific to Svelte. Same applies to the usage of on:mouseover and on:mouseout. In standard HTML or JavaScript, we use onmouseover and onmouseout.

Here are some things which I haven't checked, but you might want to consider looking at:

  • It looks like you are trying to load JSON data from an external source, but the function fetchProteins() is never called and its result (the promise returned) is not being awaited. The way it's structured now, Proteins in the returned object will hold a Promise, not the actual data.
  • The code assumes that the returned JSON data is an array stored in data.proteins, but the provided JSON example suggests that the data structure might be different. It doesn't show any 'proteins' key in the JSON. The code needs to align with the actual structure of the returned JSON data. data.proteins implies that data is an object and proteins is a property of that object containing an array.
  • In your SVG transform attribute, you are using braces {} though the correct syntax for a translate transformation in SVG is translate(x,y).

You might want to try looking at the following changes:

export const load = ({ fetch }) => {
  const fetchProteins = async () => {
    const res = await fetch('https:address/assets/ptm.json')
    const data = await res.json()
    return data
  }

  return {
    Proteins: fetchProteins
  }
}
  $: xScale = scaleLinear()
        .domain(extent(data.map((d) => d.length)))
        .range([0, 800 - (margin * 2)]);
    
    $: yScale = scaleLinear()
        .domain(extent(data.map((d) => d.nr_modifications)))
        .range([0, 400 - (margin * 2)]);
<g transform="translate({margin}px, {margin}px)">
        {#each data as datapoint}
            <circle
                cx={xScale(datapoint.length)}
                cy={yScale(datapoint.nr_modifications)}
                class="{datapoint.species === 'human' ? 'human' : ''}"
                r="20"
            />
        {/each}
        <!-- The rest of your code -->
</g>
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.