Skip to content

Commit 96d3228

Browse files
committed
fix TimeResponseData.to_pandas() for multi-trace responses
1 parent 448fb62 commit 96d3228

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

control/tests/timeresp_test.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,16 +1266,28 @@ def test_to_pandas():
12661266
np.testing.assert_equal(df['u[0]'], resp.inputs)
12671267
np.testing.assert_equal(df['y[0]'], resp.inputs * 5)
12681268

1269+
# Multi-trace data
12691270
# https://github.com/python-control/python-control/issues/1087
12701271
model = ct.rss(
12711272
states=['x0', 'x1'], outputs=['y0', 'y1'],
12721273
inputs=['u0', 'u1'], name='My Model')
12731274
T = np.linspace(0, 10, 100, endpoint=False)
12741275
X0 = np.zeros(model.nstates)
1275-
res = ct.step_response(model, T=T, X0=X0, input=0)
1276+
1277+
res = ct.step_response(model, T=T, X0=X0, input=0) # extract single trace
1278+
df = res.to_pandas()
1279+
np.testing.assert_equal(
1280+
df[df['trace'] == 'From u0']['time'], res.time)
1281+
np.testing.assert_equal(
1282+
df[df['trace'] == 'From u0']['y1'], res.outputs['y1', 0])
1283+
1284+
res = ct.step_response(model, T=T, X0=X0) # all traces
12761285
df = res.to_pandas()
1277-
np.testing.assert_equal(df['time'], res.time)
1278-
np.testing.assert_equal(df['y1'], res.outputs['y1'])
1286+
for i, label in enumerate(res.trace_labels):
1287+
np.testing.assert_equal(
1288+
df[df['trace'] == label]['time'], res.time)
1289+
np.testing.assert_equal(
1290+
df[df['trace'] == label]['u0'], res.inputs['u0', i])
12791291

12801292

12811293
@pytest.mark.skipif(pandas_check(), reason="pandas installed")

control/timeresp.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -718,25 +718,34 @@ def __len__(self):
718718
def to_pandas(self):
719719
"""Convert response data to pandas data frame.
720720
721-
Creates a pandas data frame using the input, output, and state
722-
labels for the time response.
721+
Creates a pandas data frame using the input, output, and state labels
722+
for the time response. The column labels are given by the input and
723+
output (and state, when present) labels, with time labeled by 'time'
724+
and traces (for multi-trace responses) labeled by 'trace'.
723725
724726
"""
725727
if not pandas_check():
726728
raise ImportError("pandas not installed")
727729
import pandas
728730

729731
# Create a dict for setting up the data frame
730-
data = {'time': self.time}
732+
data = {'time': np.tile(
733+
self.time, self.ntraces if self.ntraces > 0 else 1)}
734+
if self.ntraces > 0:
735+
data['trace'] = np.hstack([
736+
np.full(self.time.size, label) for label in self.trace_labels])
731737
if self.ninputs > 0:
732738
data.update(
733-
{name: self.u[i] for i, name in enumerate(self.input_labels)})
739+
{name: self.u[i].reshape(-1)
740+
for i, name in enumerate(self.input_labels)})
734741
if self.noutputs > 0:
735742
data.update(
736-
{name: self.y[i] for i, name in enumerate(self.output_labels)})
743+
{name: self.y[i].reshape(-1)
744+
for i, name in enumerate(self.output_labels)})
737745
if self.nstates > 0:
738746
data.update(
739-
{name: self.x[i] for i, name in enumerate(self.state_labels)})
747+
{name: self.x[i].reshape(-1)
748+
for i, name in enumerate(self.state_labels)})
740749

741750
return pandas.DataFrame(data)
742751

0 commit comments

Comments
 (0)