Edit on GitHub

vse_sim.dataframe

Pandas-first helpers for VSE simulation results.

  1"""Pandas-first helpers for VSE simulation results."""
  2
  3from __future__ import annotations
  4
  5from collections.abc import Iterable
  6from dataclasses import dataclass
  7from inspect import signature
  8from pathlib import Path
  9
 10import pandas as pd
 11
 12DEFAULT_GROUP_BY = ("method", "chooser")
 13
 14
 15def _group_columns(group_by) -> list[str]:
 16    return [group_by] if isinstance(group_by, str) else list(group_by)
 17
 18
 19def _unique_columns(*groups) -> tuple[str, ...]:
 20    columns = []
 21    for group in groups:
 22        for column in _group_columns(group):
 23            if column not in columns:
 24                columns.append(column)
 25    return tuple(columns)
 26
 27
 28def _voter_metadata(voter) -> dict:
 29    metadata = {}
 30    for attribute in ("cluster", "personality"):
 31        if hasattr(voter, attribute):
 32            metadata[attribute] = getattr(voter, attribute)
 33    if hasattr(voter, "dims"):
 34        for dimension, value in enumerate(voter.dims):
 35            metadata[f"dimension_{dimension}"] = value
 36    return metadata
 37
 38
 39def _call_dataframe_method(method, copy=True, **kwargs) -> pd.DataFrame:
 40    if "copy" in signature(method).parameters:
 41        return method(copy=copy, **kwargs)
 42    frame = method(**kwargs)
 43    return frame.copy() if copy else frame
 44
 45
 46def to_dataframe(data, copy=True, **kwargs) -> pd.DataFrame:
 47    """Convert VSE objects, result rows, or records to a pandas DataFrame."""
 48    if isinstance(data, VseResults):
 49        return data.to_dataframe(copy=copy)
 50    if isinstance(data, pd.DataFrame):
 51        return data.copy() if copy else data
 52    dataframe_method = getattr(data, "to_dataframe", None)
 53    if callable(dataframe_method):
 54        return _call_dataframe_method(dataframe_method, copy=copy, **kwargs)
 55    return pd.DataFrame(data, **kwargs)
 56
 57
 58def rows_to_dataframe(rows: Iterable[dict] | pd.DataFrame | VseResults, copy=True) -> pd.DataFrame:
 59    """Convert simulation rows, a DataFrame, or ``VseResults`` to a DataFrame."""
 60    return to_dataframe(rows, copy=copy)
 61
 62
 63def voter_to_dataframe(
 64    voter,
 65    voter_id=None,
 66    voter_column="voter",
 67    candidate_column="candidate",
 68    value_column="utility",
 69) -> pd.DataFrame:
 70    """Return one voter's candidate utilities as a tidy DataFrame."""
 71    metadata = _voter_metadata(voter)
 72    rows = []
 73    for candidate, value in enumerate(voter):
 74        row = {candidate_column: candidate, value_column: value, **metadata}
 75        if voter_id is not None:
 76            row[voter_column] = voter_id
 77        rows.append(row)
 78    return pd.DataFrame(rows)
 79
 80
 81def voters_to_dataframe(
 82    voters,
 83    wide=False,
 84    voter_column="voter",
 85    candidate_column="candidate",
 86    value_column="utility",
 87    candidate_prefix="candidate_",
 88) -> pd.DataFrame:
 89    """Return voter utilities as a tidy or wide DataFrame."""
 90    if wide:
 91        rows = []
 92        for voter_id, voter in enumerate(voters):
 93            row = {
 94                voter_column: voter_id,
 95                **{
 96                    f"{candidate_prefix}{candidate}": value for candidate, value in enumerate(voter)
 97                },
 98                **_voter_metadata(voter),
 99            }
100            rows.append(row)
101        return pd.DataFrame(rows)
102
103    rows = []
104    for voter_id, voter in enumerate(voters):
105        rows.extend(
106            voter_to_dataframe(
107                voter,
108                voter_id=voter_id,
109                voter_column=voter_column,
110                candidate_column=candidate_column,
111                value_column=value_column,
112            ).to_dict("records")
113        )
114    return pd.DataFrame(rows)
115
116
117def ballots_to_dataframe(
118    ballots,
119    wide=False,
120    method=None,
121    voter_column="voter",
122    candidate_column="candidate",
123    value_column="ballot",
124    candidate_prefix="candidate_",
125) -> pd.DataFrame:
126    """Return ballots as a tidy or wide DataFrame."""
127    if wide:
128        rows = []
129        for voter_id, ballot in enumerate(ballots):
130            row = {
131                voter_column: voter_id,
132                **{
133                    f"{candidate_prefix}{candidate}": value
134                    for candidate, value in enumerate(ballot)
135                },
136            }
137            if method is not None:
138                row["method"] = str(method)
139            rows.append(row)
140        return pd.DataFrame(rows)
141
142    rows = []
143    for voter_id, ballot in enumerate(ballots):
144        for candidate, value in enumerate(ballot):
145            row = {
146                voter_column: voter_id,
147                candidate_column: candidate,
148                value_column: value,
149            }
150            if method is not None:
151                row["method"] = str(method)
152            rows.append(row)
153    return pd.DataFrame(rows)
154
155
156def ballots_from_dataframe(
157    ballots,
158    voter_column="voter",
159    candidate_column="candidate",
160    value_column="ballot",
161    candidate_prefix="candidate_",
162):
163    """Convert tidy or wide ballot DataFrames back to method-ready ballots."""
164    if not isinstance(ballots, pd.DataFrame):
165        return ballots if type(ballots) is list else list(ballots)
166
167    if {voter_column, candidate_column, value_column} <= set(ballots.columns):
168        return (
169            ballots.pivot(index=voter_column, columns=candidate_column, values=value_column)
170            .sort_index()
171            .sort_index(axis=1)
172            .to_numpy()
173            .tolist()
174        )
175
176    candidate_columns = [
177        column for column in ballots.columns if str(column).startswith(candidate_prefix)
178    ]
179    if candidate_columns:
180        candidate_columns = sorted(
181            candidate_columns, key=lambda column: int(str(column).split("_")[-1])
182        )
183        return ballots[candidate_columns].to_numpy().tolist()
184
185    return ballots.to_numpy().tolist()
186
187
188def scores_to_dataframe(
189    scores,
190    method=None,
191    candidate_column="candidate",
192    value_column="score",
193) -> pd.DataFrame:
194    """Return candidate-level method scores as a DataFrame."""
195    rows = [
196        {
197            candidate_column: candidate,
198            value_column: score,
199        }
200        for candidate, score in enumerate(scores)
201    ]
202    frame = pd.DataFrame(rows)
203    if method is not None:
204        frame.insert(0, "method", str(method))
205    return frame
206
207
208def summarize_vse(
209    rows: Iterable[dict] | pd.DataFrame | VseResults,
210    group_by=DEFAULT_GROUP_BY,
211    sort_by="mean_vse",
212    ascending=False,
213) -> pd.DataFrame:
214    """Summarize VSE scores by method, chooser, or another grouping."""
215    return VseResults(rows_to_dataframe(rows, copy=False)).summarize(
216        group_by=group_by,
217        sort_by=sort_by,
218        ascending=ascending,
219    )
220
221
222def read_results_csv(path) -> "VseResults":
223    """Load a VSE result CSV written by ``CsvBatch.saveFile``."""
224    return VseResults.from_csv(path)
225
226
227@dataclass(frozen=True)
228class VseResults:
229    """Pandas-backed simulation result set.
230
231    ``frame`` is the canonical tabular representation. Convenience methods
232    return DataFrames or matplotlib axes so notebook workflows can keep chaining.
233    """
234
235    frame: pd.DataFrame
236
237    @classmethod
238    def from_rows(cls, rows: Iterable[dict] | pd.DataFrame | VseResults) -> "VseResults":
239        return cls(to_dataframe(rows))
240
241    @classmethod
242    def from_csv(cls, path) -> "VseResults":
243        return cls(pd.read_csv(Path(path), comment="#"))
244
245    @classmethod
246    def concat(cls, results: Iterable[VseResults | pd.DataFrame | Iterable[dict]]) -> "VseResults":
247        frames = [rows_to_dataframe(result) for result in results]
248        return cls(pd.concat(frames, ignore_index=True))
249
250    def __len__(self) -> int:
251        return len(self.frame)
252
253    @property
254    def dataframe(self) -> pd.DataFrame:
255        """Return the backing DataFrame for fluent notebook work."""
256        return self.frame
257
258    @property
259    def df(self) -> pd.DataFrame:
260        """Alias for ``dataframe``."""
261        return self.frame
262
263    def to_dataframe(self, copy=True) -> pd.DataFrame:
264        return self.frame.copy() if copy else self.frame
265
266    def to_csv(self, path, index=False, **kwargs):
267        """Write the result DataFrame to CSV and return the path."""
268        self.frame.to_csv(path, index=index, **kwargs)
269        return path
270
271    def summarize(
272        self,
273        group_by=DEFAULT_GROUP_BY,
274        sort_by="mean_vse",
275        ascending=False,
276    ) -> pd.DataFrame:
277        """Return aggregate VSE metrics grouped by one or more columns."""
278        group_columns = _group_columns(group_by)
279        summary = (
280            self.frame.groupby(group_columns, as_index=False)
281            .agg(
282                rows=("vse", "size"),
283                elections=("eid", "nunique"),
284                mean_vse=("vse", "mean"),
285                median_vse=("vse", "median"),
286                min_vse=("vse", "min"),
287                max_vse=("vse", "max"),
288                std_vse=("vse", "std"),
289            )
290            .fillna({"std_vse": 0})
291        )
292        return summary.sort_values(
293            [sort_by, *group_columns],
294            ascending=[ascending, *[True] * len(group_columns)],
295        ).reset_index(drop=True)
296
297    def leaderboard(self, n=10, group_by="method", by="mean_vse") -> pd.DataFrame:
298        """Return the top groups by a summary metric."""
299        return self.summarize(group_by=group_by, sort_by=by).head(n)
300
301    def pivot(
302        self,
303        index="method",
304        columns="chooser",
305        values="mean_vse",
306        group_by=None,
307    ) -> pd.DataFrame:
308        """Return a comparison matrix from summarized result data."""
309        index_columns = _group_columns(index)
310        column_columns = _group_columns(columns)
311        if group_by is None:
312            group_by = _unique_columns(index, columns)
313        summary = self.summarize(group_by=group_by).copy()
314        pivot_columns = []
315        for column in column_columns:
316            if column in index_columns:
317                column_alias = f"__vse_sim_pivot_{column}"
318                summary[column_alias] = summary[column]
319                pivot_columns.append(column_alias)
320            else:
321                pivot_columns.append(column)
322        pivoted = summary.pivot(
323            index=index,
324            columns=pivot_columns[0] if isinstance(columns, str) else pivot_columns,
325            values=values,
326        )
327        pivoted.columns = pivoted.columns.set_names(column_columns)
328        return pivoted
329
330    def report(self, group_by=DEFAULT_GROUP_BY) -> dict[str, pd.DataFrame]:
331        """Build common report tables from a result set."""
332        tables = {
333            "results": self.to_dataframe(),
334            "summary": self.summarize(group_by=group_by),
335            "method_summary": self.summarize(group_by="method"),
336        }
337        if "chooser" in self.frame:
338            tables["chooser_summary"] = self.summarize(group_by="chooser")
339            tables["method_by_chooser"] = self.pivot()
340        return tables
341
342    def plot_vse(
343        self,
344        group_by="method",
345        value="mean_vse",
346        kind="bar",
347        ax=None,
348        title=None,
349        **kwargs,
350    ):
351        """Plot summarized VSE scores and return the matplotlib axes."""
352        summary = self.summarize(group_by=group_by)
353        group_columns = _group_columns(group_by)
354        labels = summary[group_columns].astype(str).agg(" | ".join, axis=1)
355        plot_frame = summary.assign(label=labels).set_index("label")
356        axes = plot_frame[value].plot(kind=kind, ax=ax, **kwargs)
357        axes.set_xlabel("VSE" if kind == "barh" else "")
358        axes.set_ylabel("" if kind == "barh" else "VSE")
359        axes.set_title(title or f"{value} by {' / '.join(group_columns)}")
360        return axes
361
362
363__all__ = [
364    "DEFAULT_GROUP_BY",
365    "VseResults",
366    "ballots_from_dataframe",
367    "ballots_to_dataframe",
368    "read_results_csv",
369    "rows_to_dataframe",
370    "scores_to_dataframe",
371    "summarize_vse",
372    "to_dataframe",
373    "voter_to_dataframe",
374    "voters_to_dataframe",
375]
DEFAULT_GROUP_BY = ('method', 'chooser')
@dataclass(frozen=True)
class VseResults:
228@dataclass(frozen=True)
229class VseResults:
230    """Pandas-backed simulation result set.
231
232    ``frame`` is the canonical tabular representation. Convenience methods
233    return DataFrames or matplotlib axes so notebook workflows can keep chaining.
234    """
235
236    frame: pd.DataFrame
237
238    @classmethod
239    def from_rows(cls, rows: Iterable[dict] | pd.DataFrame | VseResults) -> "VseResults":
240        return cls(to_dataframe(rows))
241
242    @classmethod
243    def from_csv(cls, path) -> "VseResults":
244        return cls(pd.read_csv(Path(path), comment="#"))
245
246    @classmethod
247    def concat(cls, results: Iterable[VseResults | pd.DataFrame | Iterable[dict]]) -> "VseResults":
248        frames = [rows_to_dataframe(result) for result in results]
249        return cls(pd.concat(frames, ignore_index=True))
250
251    def __len__(self) -> int:
252        return len(self.frame)
253
254    @property
255    def dataframe(self) -> pd.DataFrame:
256        """Return the backing DataFrame for fluent notebook work."""
257        return self.frame
258
259    @property
260    def df(self) -> pd.DataFrame:
261        """Alias for ``dataframe``."""
262        return self.frame
263
264    def to_dataframe(self, copy=True) -> pd.DataFrame:
265        return self.frame.copy() if copy else self.frame
266
267    def to_csv(self, path, index=False, **kwargs):
268        """Write the result DataFrame to CSV and return the path."""
269        self.frame.to_csv(path, index=index, **kwargs)
270        return path
271
272    def summarize(
273        self,
274        group_by=DEFAULT_GROUP_BY,
275        sort_by="mean_vse",
276        ascending=False,
277    ) -> pd.DataFrame:
278        """Return aggregate VSE metrics grouped by one or more columns."""
279        group_columns = _group_columns(group_by)
280        summary = (
281            self.frame.groupby(group_columns, as_index=False)
282            .agg(
283                rows=("vse", "size"),
284                elections=("eid", "nunique"),
285                mean_vse=("vse", "mean"),
286                median_vse=("vse", "median"),
287                min_vse=("vse", "min"),
288                max_vse=("vse", "max"),
289                std_vse=("vse", "std"),
290            )
291            .fillna({"std_vse": 0})
292        )
293        return summary.sort_values(
294            [sort_by, *group_columns],
295            ascending=[ascending, *[True] * len(group_columns)],
296        ).reset_index(drop=True)
297
298    def leaderboard(self, n=10, group_by="method", by="mean_vse") -> pd.DataFrame:
299        """Return the top groups by a summary metric."""
300        return self.summarize(group_by=group_by, sort_by=by).head(n)
301
302    def pivot(
303        self,
304        index="method",
305        columns="chooser",
306        values="mean_vse",
307        group_by=None,
308    ) -> pd.DataFrame:
309        """Return a comparison matrix from summarized result data."""
310        index_columns = _group_columns(index)
311        column_columns = _group_columns(columns)
312        if group_by is None:
313            group_by = _unique_columns(index, columns)
314        summary = self.summarize(group_by=group_by).copy()
315        pivot_columns = []
316        for column in column_columns:
317            if column in index_columns:
318                column_alias = f"__vse_sim_pivot_{column}"
319                summary[column_alias] = summary[column]
320                pivot_columns.append(column_alias)
321            else:
322                pivot_columns.append(column)
323        pivoted = summary.pivot(
324            index=index,
325            columns=pivot_columns[0] if isinstance(columns, str) else pivot_columns,
326            values=values,
327        )
328        pivoted.columns = pivoted.columns.set_names(column_columns)
329        return pivoted
330
331    def report(self, group_by=DEFAULT_GROUP_BY) -> dict[str, pd.DataFrame]:
332        """Build common report tables from a result set."""
333        tables = {
334            "results": self.to_dataframe(),
335            "summary": self.summarize(group_by=group_by),
336            "method_summary": self.summarize(group_by="method"),
337        }
338        if "chooser" in self.frame:
339            tables["chooser_summary"] = self.summarize(group_by="chooser")
340            tables["method_by_chooser"] = self.pivot()
341        return tables
342
343    def plot_vse(
344        self,
345        group_by="method",
346        value="mean_vse",
347        kind="bar",
348        ax=None,
349        title=None,
350        **kwargs,
351    ):
352        """Plot summarized VSE scores and return the matplotlib axes."""
353        summary = self.summarize(group_by=group_by)
354        group_columns = _group_columns(group_by)
355        labels = summary[group_columns].astype(str).agg(" | ".join, axis=1)
356        plot_frame = summary.assign(label=labels).set_index("label")
357        axes = plot_frame[value].plot(kind=kind, ax=ax, **kwargs)
358        axes.set_xlabel("VSE" if kind == "barh" else "")
359        axes.set_ylabel("" if kind == "barh" else "VSE")
360        axes.set_title(title or f"{value} by {' / '.join(group_columns)}")
361        return axes

Pandas-backed simulation result set.

frame is the canonical tabular representation. Convenience methods return DataFrames or matplotlib axes so notebook workflows can keep chaining.

VseResults(frame: pandas.DataFrame)
frame: pandas.DataFrame
@classmethod
def from_rows( cls, rows: Iterable[dict] | pandas.DataFrame | VseResults) -> VseResults:
238    @classmethod
239    def from_rows(cls, rows: Iterable[dict] | pd.DataFrame | VseResults) -> "VseResults":
240        return cls(to_dataframe(rows))
@classmethod
def from_csv(cls, path) -> VseResults:
242    @classmethod
243    def from_csv(cls, path) -> "VseResults":
244        return cls(pd.read_csv(Path(path), comment="#"))
@classmethod
def concat( cls, results: Iterable[VseResults | pandas.DataFrame | Iterable[dict]]) -> VseResults:
246    @classmethod
247    def concat(cls, results: Iterable[VseResults | pd.DataFrame | Iterable[dict]]) -> "VseResults":
248        frames = [rows_to_dataframe(result) for result in results]
249        return cls(pd.concat(frames, ignore_index=True))
dataframe: pandas.DataFrame
254    @property
255    def dataframe(self) -> pd.DataFrame:
256        """Return the backing DataFrame for fluent notebook work."""
257        return self.frame

Return the backing DataFrame for fluent notebook work.

df: pandas.DataFrame
259    @property
260    def df(self) -> pd.DataFrame:
261        """Alias for ``dataframe``."""
262        return self.frame

Alias for dataframe.

def to_dataframe(self, copy=True) -> pandas.DataFrame:
264    def to_dataframe(self, copy=True) -> pd.DataFrame:
265        return self.frame.copy() if copy else self.frame
def to_csv(self, path, index=False, **kwargs):
267    def to_csv(self, path, index=False, **kwargs):
268        """Write the result DataFrame to CSV and return the path."""
269        self.frame.to_csv(path, index=index, **kwargs)
270        return path

Write the result DataFrame to CSV and return the path.

def summarize( self, group_by=('method', 'chooser'), sort_by='mean_vse', ascending=False) -> pandas.DataFrame:
272    def summarize(
273        self,
274        group_by=DEFAULT_GROUP_BY,
275        sort_by="mean_vse",
276        ascending=False,
277    ) -> pd.DataFrame:
278        """Return aggregate VSE metrics grouped by one or more columns."""
279        group_columns = _group_columns(group_by)
280        summary = (
281            self.frame.groupby(group_columns, as_index=False)
282            .agg(
283                rows=("vse", "size"),
284                elections=("eid", "nunique"),
285                mean_vse=("vse", "mean"),
286                median_vse=("vse", "median"),
287                min_vse=("vse", "min"),
288                max_vse=("vse", "max"),
289                std_vse=("vse", "std"),
290            )
291            .fillna({"std_vse": 0})
292        )
293        return summary.sort_values(
294            [sort_by, *group_columns],
295            ascending=[ascending, *[True] * len(group_columns)],
296        ).reset_index(drop=True)

Return aggregate VSE metrics grouped by one or more columns.

def leaderboard(self, n=10, group_by='method', by='mean_vse') -> pandas.DataFrame:
298    def leaderboard(self, n=10, group_by="method", by="mean_vse") -> pd.DataFrame:
299        """Return the top groups by a summary metric."""
300        return self.summarize(group_by=group_by, sort_by=by).head(n)

Return the top groups by a summary metric.

def pivot( self, index='method', columns='chooser', values='mean_vse', group_by=None) -> pandas.DataFrame:
302    def pivot(
303        self,
304        index="method",
305        columns="chooser",
306        values="mean_vse",
307        group_by=None,
308    ) -> pd.DataFrame:
309        """Return a comparison matrix from summarized result data."""
310        index_columns = _group_columns(index)
311        column_columns = _group_columns(columns)
312        if group_by is None:
313            group_by = _unique_columns(index, columns)
314        summary = self.summarize(group_by=group_by).copy()
315        pivot_columns = []
316        for column in column_columns:
317            if column in index_columns:
318                column_alias = f"__vse_sim_pivot_{column}"
319                summary[column_alias] = summary[column]
320                pivot_columns.append(column_alias)
321            else:
322                pivot_columns.append(column)
323        pivoted = summary.pivot(
324            index=index,
325            columns=pivot_columns[0] if isinstance(columns, str) else pivot_columns,
326            values=values,
327        )
328        pivoted.columns = pivoted.columns.set_names(column_columns)
329        return pivoted

Return a comparison matrix from summarized result data.

def report(self, group_by=('method', 'chooser')) -> dict[str, pandas.DataFrame]:
331    def report(self, group_by=DEFAULT_GROUP_BY) -> dict[str, pd.DataFrame]:
332        """Build common report tables from a result set."""
333        tables = {
334            "results": self.to_dataframe(),
335            "summary": self.summarize(group_by=group_by),
336            "method_summary": self.summarize(group_by="method"),
337        }
338        if "chooser" in self.frame:
339            tables["chooser_summary"] = self.summarize(group_by="chooser")
340            tables["method_by_chooser"] = self.pivot()
341        return tables

Build common report tables from a result set.

def plot_vse( self, group_by='method', value='mean_vse', kind='bar', ax=None, title=None, **kwargs):
343    def plot_vse(
344        self,
345        group_by="method",
346        value="mean_vse",
347        kind="bar",
348        ax=None,
349        title=None,
350        **kwargs,
351    ):
352        """Plot summarized VSE scores and return the matplotlib axes."""
353        summary = self.summarize(group_by=group_by)
354        group_columns = _group_columns(group_by)
355        labels = summary[group_columns].astype(str).agg(" | ".join, axis=1)
356        plot_frame = summary.assign(label=labels).set_index("label")
357        axes = plot_frame[value].plot(kind=kind, ax=ax, **kwargs)
358        axes.set_xlabel("VSE" if kind == "barh" else "")
359        axes.set_ylabel("" if kind == "barh" else "VSE")
360        axes.set_title(title or f"{value} by {' / '.join(group_columns)}")
361        return axes

Plot summarized VSE scores and return the matplotlib axes.

def ballots_from_dataframe( ballots, voter_column='voter', candidate_column='candidate', value_column='ballot', candidate_prefix='candidate_'):
157def ballots_from_dataframe(
158    ballots,
159    voter_column="voter",
160    candidate_column="candidate",
161    value_column="ballot",
162    candidate_prefix="candidate_",
163):
164    """Convert tidy or wide ballot DataFrames back to method-ready ballots."""
165    if not isinstance(ballots, pd.DataFrame):
166        return ballots if type(ballots) is list else list(ballots)
167
168    if {voter_column, candidate_column, value_column} <= set(ballots.columns):
169        return (
170            ballots.pivot(index=voter_column, columns=candidate_column, values=value_column)
171            .sort_index()
172            .sort_index(axis=1)
173            .to_numpy()
174            .tolist()
175        )
176
177    candidate_columns = [
178        column for column in ballots.columns if str(column).startswith(candidate_prefix)
179    ]
180    if candidate_columns:
181        candidate_columns = sorted(
182            candidate_columns, key=lambda column: int(str(column).split("_")[-1])
183        )
184        return ballots[candidate_columns].to_numpy().tolist()
185
186    return ballots.to_numpy().tolist()

Convert tidy or wide ballot DataFrames back to method-ready ballots.

def ballots_to_dataframe( ballots, wide=False, method=None, voter_column='voter', candidate_column='candidate', value_column='ballot', candidate_prefix='candidate_') -> pandas.DataFrame:
118def ballots_to_dataframe(
119    ballots,
120    wide=False,
121    method=None,
122    voter_column="voter",
123    candidate_column="candidate",
124    value_column="ballot",
125    candidate_prefix="candidate_",
126) -> pd.DataFrame:
127    """Return ballots as a tidy or wide DataFrame."""
128    if wide:
129        rows = []
130        for voter_id, ballot in enumerate(ballots):
131            row = {
132                voter_column: voter_id,
133                **{
134                    f"{candidate_prefix}{candidate}": value
135                    for candidate, value in enumerate(ballot)
136                },
137            }
138            if method is not None:
139                row["method"] = str(method)
140            rows.append(row)
141        return pd.DataFrame(rows)
142
143    rows = []
144    for voter_id, ballot in enumerate(ballots):
145        for candidate, value in enumerate(ballot):
146            row = {
147                voter_column: voter_id,
148                candidate_column: candidate,
149                value_column: value,
150            }
151            if method is not None:
152                row["method"] = str(method)
153            rows.append(row)
154    return pd.DataFrame(rows)

Return ballots as a tidy or wide DataFrame.

def read_results_csv(path) -> VseResults:
223def read_results_csv(path) -> "VseResults":
224    """Load a VSE result CSV written by ``CsvBatch.saveFile``."""
225    return VseResults.from_csv(path)

Load a VSE result CSV written by CsvBatch.saveFile.

def rows_to_dataframe( rows: Iterable[dict] | pandas.DataFrame | VseResults, copy=True) -> pandas.DataFrame:
59def rows_to_dataframe(rows: Iterable[dict] | pd.DataFrame | VseResults, copy=True) -> pd.DataFrame:
60    """Convert simulation rows, a DataFrame, or ``VseResults`` to a DataFrame."""
61    return to_dataframe(rows, copy=copy)

Convert simulation rows, a DataFrame, or VseResults to a DataFrame.

def scores_to_dataframe( scores, method=None, candidate_column='candidate', value_column='score') -> pandas.DataFrame:
189def scores_to_dataframe(
190    scores,
191    method=None,
192    candidate_column="candidate",
193    value_column="score",
194) -> pd.DataFrame:
195    """Return candidate-level method scores as a DataFrame."""
196    rows = [
197        {
198            candidate_column: candidate,
199            value_column: score,
200        }
201        for candidate, score in enumerate(scores)
202    ]
203    frame = pd.DataFrame(rows)
204    if method is not None:
205        frame.insert(0, "method", str(method))
206    return frame

Return candidate-level method scores as a DataFrame.

def summarize_vse( rows: Iterable[dict] | pandas.DataFrame | VseResults, group_by=('method', 'chooser'), sort_by='mean_vse', ascending=False) -> pandas.DataFrame:
209def summarize_vse(
210    rows: Iterable[dict] | pd.DataFrame | VseResults,
211    group_by=DEFAULT_GROUP_BY,
212    sort_by="mean_vse",
213    ascending=False,
214) -> pd.DataFrame:
215    """Summarize VSE scores by method, chooser, or another grouping."""
216    return VseResults(rows_to_dataframe(rows, copy=False)).summarize(
217        group_by=group_by,
218        sort_by=sort_by,
219        ascending=ascending,
220    )

Summarize VSE scores by method, chooser, or another grouping.

def to_dataframe(data, copy=True, **kwargs) -> pandas.DataFrame:
47def to_dataframe(data, copy=True, **kwargs) -> pd.DataFrame:
48    """Convert VSE objects, result rows, or records to a pandas DataFrame."""
49    if isinstance(data, VseResults):
50        return data.to_dataframe(copy=copy)
51    if isinstance(data, pd.DataFrame):
52        return data.copy() if copy else data
53    dataframe_method = getattr(data, "to_dataframe", None)
54    if callable(dataframe_method):
55        return _call_dataframe_method(dataframe_method, copy=copy, **kwargs)
56    return pd.DataFrame(data, **kwargs)

Convert VSE objects, result rows, or records to a pandas DataFrame.

def voter_to_dataframe( voter, voter_id=None, voter_column='voter', candidate_column='candidate', value_column='utility') -> pandas.DataFrame:
64def voter_to_dataframe(
65    voter,
66    voter_id=None,
67    voter_column="voter",
68    candidate_column="candidate",
69    value_column="utility",
70) -> pd.DataFrame:
71    """Return one voter's candidate utilities as a tidy DataFrame."""
72    metadata = _voter_metadata(voter)
73    rows = []
74    for candidate, value in enumerate(voter):
75        row = {candidate_column: candidate, value_column: value, **metadata}
76        if voter_id is not None:
77            row[voter_column] = voter_id
78        rows.append(row)
79    return pd.DataFrame(rows)

Return one voter's candidate utilities as a tidy DataFrame.

def voters_to_dataframe( voters, wide=False, voter_column='voter', candidate_column='candidate', value_column='utility', candidate_prefix='candidate_') -> pandas.DataFrame:
 82def voters_to_dataframe(
 83    voters,
 84    wide=False,
 85    voter_column="voter",
 86    candidate_column="candidate",
 87    value_column="utility",
 88    candidate_prefix="candidate_",
 89) -> pd.DataFrame:
 90    """Return voter utilities as a tidy or wide DataFrame."""
 91    if wide:
 92        rows = []
 93        for voter_id, voter in enumerate(voters):
 94            row = {
 95                voter_column: voter_id,
 96                **{
 97                    f"{candidate_prefix}{candidate}": value for candidate, value in enumerate(voter)
 98                },
 99                **_voter_metadata(voter),
100            }
101            rows.append(row)
102        return pd.DataFrame(rows)
103
104    rows = []
105    for voter_id, voter in enumerate(voters):
106        rows.extend(
107            voter_to_dataframe(
108                voter,
109                voter_id=voter_id,
110                voter_column=voter_column,
111                candidate_column=candidate_column,
112                value_column=value_column,
113            ).to_dict("records")
114        )
115    return pd.DataFrame(rows)

Return voter utilities as a tidy or wide DataFrame.