| Conditions | 6 |
| Total Lines | 138 |
| Code Lines | 85 |
| Lines | 12 |
| Ratio | 8.7 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | """ |
||
| 137 | def evaluate_df(self, df, ctx): |
||
| 138 | """ |
||
| 139 | Evaluate electrical load sector breakdown. |
||
| 140 | |||
| 141 | Parameters |
||
| 142 | ---------- |
||
| 143 | df : pd.DataFrame |
||
| 144 | DataFrame with total load_twh column |
||
| 145 | ctx : dict |
||
| 146 | Context information |
||
| 147 | |||
| 148 | Returns |
||
| 149 | ------- |
||
| 150 | RuleResult |
||
| 151 | Validation result with success/failure status |
||
| 152 | """ |
||
| 153 | View Code Duplication | if df.empty or df["load_twh"].isna().all(): |
|
|
|
|||
| 154 | return RuleResult( |
||
| 155 | rule_id=self.rule_id, |
||
| 156 | task=self.task, |
||
| 157 | table=self.table, |
||
| 158 | kind=self.kind, |
||
| 159 | success=False, |
||
| 160 | message=f"No electrical load data found for scenario {self.scenario}", |
||
| 161 | severity=Severity.ERROR, |
||
| 162 | schema=self.schema, |
||
| 163 | table_name=self.table_name, |
||
| 164 | rule_class=self.__class__.__name__ |
||
| 165 | ) |
||
| 166 | |||
| 167 | # Get total AC load |
||
| 168 | total_load_twh = float(df["load_twh"].values[0]) |
||
| 169 | |||
| 170 | # Get sector loads |
||
| 171 | try: |
||
| 172 | sector_loads = self._get_sector_loads() |
||
| 173 | except Exception as e: |
||
| 174 | return RuleResult( |
||
| 175 | rule_id=self.rule_id, |
||
| 176 | task=self.task, |
||
| 177 | table=self.table, |
||
| 178 | kind=self.kind, |
||
| 179 | success=False, |
||
| 180 | message=f"Error reading sector load data: {str(e)}", |
||
| 181 | severity=Severity.ERROR, |
||
| 182 | schema=self.schema, |
||
| 183 | table_name=self.table_name, |
||
| 184 | rule_class=self.__class__.__name__ |
||
| 185 | ) |
||
| 186 | |||
| 187 | # Expected values (from original sanity_checks.py lines 2689-2694) |
||
| 188 | # References: |
||
| 189 | # https://github.com/openego/powerd-data/blob/56b8215928a8dc4fe953d266c563ce0ed98e93f9/src/egon/data/datasets/demandregio/__init__.py#L480 |
||
| 190 | # https://github.com/openego/powerd-data/blob/56b8215928a8dc4fe953d266c563ce0ed98e93f9/src/egon/data/datasets/demandregio/__init__.py#L775 |
||
| 191 | expected_values = { |
||
| 192 | "residential": 90.4, |
||
| 193 | "commercial": 146.7, |
||
| 194 | "industrial": 382.9, |
||
| 195 | "total": 620.0 |
||
| 196 | } |
||
| 197 | |||
| 198 | # Build load summary dataframe |
||
| 199 | load_summary = pd.DataFrame({ |
||
| 200 | "sector": ["residential", "commercial", "industrial", "total"], |
||
| 201 | "expected": [ |
||
| 202 | expected_values["residential"], |
||
| 203 | expected_values["commercial"], |
||
| 204 | expected_values["industrial"], |
||
| 205 | expected_values["total"] |
||
| 206 | ], |
||
| 207 | "observed": [ |
||
| 208 | sector_loads["residential"], |
||
| 209 | sector_loads["commercial"], |
||
| 210 | sector_loads["industrial"], |
||
| 211 | total_load_twh |
||
| 212 | ] |
||
| 213 | }) |
||
| 214 | |||
| 215 | load_summary["diff"] = load_summary["observed"] - load_summary["expected"] |
||
| 216 | load_summary["diff_pct"] = ( |
||
| 217 | load_summary["diff"] / load_summary["observed"] * 100 |
||
| 218 | ) |
||
| 219 | |||
| 220 | # Check if all deviations are within tolerance (< 1% as in original) |
||
| 221 | violations = load_summary[load_summary["diff_pct"].abs() >= (self.rtol * 100)] |
||
| 222 | |||
| 223 | if not violations.empty: |
||
| 224 | # Format violation details |
||
| 225 | violation_details = [] |
||
| 226 | for _, row in violations.iterrows(): |
||
| 227 | violation_details.append( |
||
| 228 | f"{row['sector']}: {row['observed']:.2f} TWh " |
||
| 229 | f"(expected {row['expected']:.2f} TWh, " |
||
| 230 | f"deviation {row['diff_pct']:+.2f}%)" |
||
| 231 | ) |
||
| 232 | |||
| 233 | max_deviation = load_summary["diff_pct"].abs().max() |
||
| 234 | |||
| 235 | return RuleResult( |
||
| 236 | rule_id=self.rule_id, |
||
| 237 | task=self.task, |
||
| 238 | table=self.table, |
||
| 239 | kind=self.kind, |
||
| 240 | success=False, |
||
| 241 | observed=float(max_deviation), |
||
| 242 | expected=self.rtol * 100, |
||
| 243 | message=( |
||
| 244 | f"Electrical load sector breakdown deviations exceed tolerance for {self.scenario}: " |
||
| 245 | f"{'; '.join(violation_details)}" |
||
| 246 | ), |
||
| 247 | severity=Severity.ERROR, |
||
| 248 | schema=self.schema, |
||
| 249 | table_name=self.table_name, |
||
| 250 | rule_class=self.__class__.__name__ |
||
| 251 | ) |
||
| 252 | |||
| 253 | # All sectors within tolerance |
||
| 254 | sector_summary = "; ".join([ |
||
| 255 | f"{row['sector']}: {row['observed']:.2f} TWh " |
||
| 256 | f"(expected {row['expected']:.2f} TWh, " |
||
| 257 | f"deviation {row['diff_pct']:+.2f}%)" |
||
| 258 | for _, row in load_summary.iterrows() |
||
| 259 | ]) |
||
| 260 | |||
| 261 | return RuleResult( |
||
| 262 | rule_id=self.rule_id, |
||
| 263 | task=self.task, |
||
| 264 | table=self.table, |
||
| 265 | kind=self.kind, |
||
| 266 | success=True, |
||
| 267 | observed=0.0, |
||
| 268 | expected=0.0, |
||
| 269 | message=( |
||
| 270 | f"Electrical load sector breakdown valid for {self.scenario}: {sector_summary}" |
||
| 271 | ), |
||
| 272 | schema=self.schema, |
||
| 273 | table_name=self.table_name, |
||
| 274 | rule_class=self.__class__.__name__ |
||
| 275 | ) |
||
| 276 |