Tables and records¶
On this page¶
- Why records are separate from elements
- Dataframe to records
- Records to dataframe
- Returned graph records
- Missing and nested values
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.

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.