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]
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.
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.
259 @property 260 def df(self) -> pd.DataFrame: 261 """Alias for ``dataframe``.""" 262 return self.frame
Alias for dataframe.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.