Skip to content

Tables and records

On this page

Why records are separate from elements

A record is a table-like dictionary. A graph element wraps business fields inside data and may add graph-specific fields such as position. Conversion helpers make both forms convenient in pandas without assuming how rows become nodes and relationships.

Dataframe to records

import pandas as pd

from st_graph_workbench import dataframe_to_records, records_to_dataframe

sightings = pd.DataFrame(
    [
        {
            "vehicle_id": "ABC123",
            "location": "Harbour camera 7",
            "observed_at": "2024-09-02 08:14",
            "confidence": 0.96,
        },
        {
            "vehicle_id": "12VEC",
            "location": "Depot gate",
            "observed_at": "2024-09-02 09:10",
            "confidence": None,
        },
    ]
)

records = dataframe_to_records(sightings, drop_missing=True)
round_trip = records_to_dataframe({"sightings": records})

print(records)
print(round_trip.to_dict(orient="records"))

dataframe_to_records() supports pandas, Polars, and Arrow-like objects that expose a common record export method. It normalizes missing scalar values, dates, decimals, tuples, sets, and array-like values into JSON-friendly data.

With drop_missing=True, the second sighting omits confidence instead of returning it as None:

[
  {
    "vehicle_id": "ABC123",
    "location": "Harbour camera 7",
    "observed_at": "2024-09-02 08:14",
    "confidence": 0.96
  },
  {
    "vehicle_id": "12VEC",
    "location": "Depot gate",
    "observed_at": "2024-09-02 09:10"
  }
]

Records to dataframe

records_to_dataframe() accepts four useful shapes:

  • A list of ordinary record dictionaries.
  • Named groups such as {"sightings": [...], "vehicles": [...]}.
  • Full graph elements such as {"nodes": [...], "edges": [...]}.
  • Element records returned by selection or search events.

group_key="record_group" names the provenance column. Set flatten_data=False when the nested Cytoscape data object should remain in one dataframe cell instead of becoming columns.

Record conversion tables

Source records, JSON-ready dictionaries, and flattened graph element rows.

Returned graph records

import json
from pathlib import Path

import streamlit as st

from st_graph_workbench import graph_workbench, records_to_dataframe

elements = json.loads(
    Path("examples/data/intelligence_case.json").read_text(encoding="utf-8")
)
event = graph_workbench(
    elements,
    selection_mode="box",
    return_selection=True,
    # Keep selection events and the Python table without an automatic inspector.
    show_selection_details=False,
    search=True,
    key="investigation-selection",
)

event_data = event.get("data", {}) if isinstance(event, dict) else {}
returned_records = (
    event_data.get("selected_elements") or event_data.get("matched_elements") or []
)

st.dataframe(records_to_dataframe(returned_records), hide_index=True)

This pattern turns the analyst's current graph selection into rows that can be filtered, joined with source data, summarized, reviewed, or exported.

Missing and nested values

Missing scalar values become None unless dropped. Nested mappings remain mappings; sequences become JSON-friendly lists. Non-string dictionary keys are converted to strings. Unsupported dataframe objects and non-dictionary rows raise TypeError with the failing boundary identified.

Conclusion

Use dataframe helpers at the application boundary, then deliberately construct graph elements with stable IDs and relationships. The helpers normalize data; they do not infer graph topology.