Build a Sounding From Your Own Data#

Estimated reading time: 5 minutes

Read a Sounding From an Archive covers the archives tephpy reads. This page covers the other way in: your data is already in Python, in a pandas.DataFrame or an xarray.Dataset, and you want a Sounding out of it.

Both libraries are hard dependencies, so both routes are available in every install with no extra to enable.

From a DataFrame#

Sounding.from_dataframe(...) reads bare arrays out of the columns. Nothing in a DataFrame carries a unit, so the units= mapping is required for every field present:

import pandas as pd

from tephpy import Sounding

df = pd.DataFrame(
    {
        "pressure": [1000.0, 925.0, 850.0, 700.0, 607.0,
                     500.0, 400.0, 300.0, 250.0, 200.0],
        "temperature": [15.4, 15.4, 10.4, 4.4, -1.9,
                        -11.9, -22.1, -39.1, -47.9, -55.7],
        "dewpoint": [14.4, 2.4, 3.4, -17.6, -24.9,
                     -37.9, -43.1, -50.1, -57.9, -71.7],
    }
)
units = {"pressure": "hPa", "temperature": "degC", "dewpoint": "degC"}
from_frame = Sounding.from_dataframe(df, units=units)

Ten levels of the Camborne ascent, enough to draw. Column names that already match the field names need no mapping at all.

Naming Your Own Columns#

When they do not match, name them. Each keyword is a field, and its value is the column:

renamed = df.rename(
    columns={"pressure": "p", "temperature": "T", "dewpoint": "Td"}
)
from_renamed = Sounding.from_dataframe(
    renamed, units=units, pressure="p", temperature="T", dewpoint="Td"
)

Note which name units is keyed by. It is the field, not the column, so the same mapping serves both calls above. pressure and temperature are required; a missing or mistyped column raises KeyError naming both names — mistype the renamed frame’s temperature column above as "Temp" rather than "T", and it raises:

KeyError: "column 'Temp' (field 'temperature') is not in the DataFrame"

Any keyword that names no known field raises TypeError instead, naming the unknown field and the fields it does know. The catch-all parameter that absorbs it is called column_map here, var_map on from_dataset():

TypeError: unknown field(s) ['bogus']; expected ['dewpoint', 'pressure',
'temperature', 'wind_direction', 'wind_speed']

From a Dataset#

Sounding.from_dataset(...) differs in one way worth knowing, because it is invisible from the signature. An xarray.Dataset can carry units, in each variable’s attrs["units"], and the constructor reads them by that convention. Here units= is the override rather than the requirement, and a CF-compliant dataset needs none:

import xarray as xr

ds = xr.Dataset(
    {
        "temperature": (
            "level", df["temperature"].to_numpy(), {"units": "degC"}
        ),
        "dewpoint": (
            "level", df["dewpoint"].to_numpy(), {"units": "degC"}
        ),
    },
    coords={
        "level": ("level", df["pressure"].to_numpy(), {"units": "hPa"})
    },
)
from_dataset = Sounding.from_dataset(ds, pressure="level")

Coordinates count as variables, which is why pressure="level" reaches one. A missing required or explicitly mapped variable raises KeyError, just as a missing column does for from_dataframe():

KeyError: "variable 'temperature' (field 'temperature') not in the Dataset"

A field with neither attrs["units"] nor a units= entry raises TephpyUnitsError — one of tephpy.exceptions — and the message carries the fix:

'temperature' (variable 'temperature') has no attrs['units'] and no
override: add units={"temperature": "<unit>"}

Naming the Ascent#

Both constructors take station=, time= and label=. Give the first two and the legend label derives, exactly as it does for a sounding read from an archive:

named = Sounding.from_dataframe(
    df,
    units=units,
    station="03808",
    time=pd.Timestamp("2026-07-21 12:00"),
)

time here is stricter than the readers of Read a Sounding From an Archive, which parse a string. This one wants a real timestamp — pandas.Timestamp, numpy.datetime64 or datetime.datetime — and a string raises TypeError. label= overrides the derived text outright.

Plotted Like Any Other#

What comes out is a Sounding, and nothing downstream can tell it was built rather than read:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(subplot_kw={"projection": "tephigram"})
ax.plot_sounding(named)
ax.legend()
../_images/build-a-sounding-plotted.png

Where to Go Next#

Work With Units covers what those unit strings may say, and what you get back. Read a Sounding From an Archive is the other route in, for data still in a file.