|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import numbers |
| 4 | + |
| 5 | +import numpy as np |
| 6 | + |
| 7 | +import uproot |
| 8 | + |
| 9 | + |
| 10 | +def _as_TGraph( |
| 11 | + x, |
| 12 | + y, |
| 13 | + x_errors=None, |
| 14 | + y_errors=None, |
| 15 | + x_errors_low=None, |
| 16 | + x_errors_high=None, |
| 17 | + y_errors_low=None, |
| 18 | + y_errors_high=None, |
| 19 | + title="", |
| 20 | + xAxisLabel="", |
| 21 | + yAxisLabel="", |
| 22 | + minY=None, |
| 23 | + maxY=None, |
| 24 | + lineColor: int = 602, |
| 25 | + lineStyle: int = 1, |
| 26 | + lineWidth: int = 1, |
| 27 | + fillColor: int = 0, |
| 28 | + fillStyle: int = 1001, |
| 29 | + markerColor: int = 1, |
| 30 | + markerStyle: int = 1, |
| 31 | + markerSize: float = 1.0, |
| 32 | +): |
| 33 | + """ |
| 34 | + Args: |
| 35 | + x (1D numpy.ndarray): x values of TGraph (length of x and y has to be the same). |
| 36 | + y (1D numpy.ndarray): y values of TGraph (length of x and y has to be the same). |
| 37 | + x_errors(None or 1D numpy.ndarray): Symethrical values of errors for corresponding x value (length of x_errors has to be the same as x and y) |
| 38 | + y_errors(None or 1D numpy.ndarray): Symethrical values of errors for corresponding y value (length of y_errors has to be the same as x and y) |
| 39 | + x_errors_low(None or 1D numpy.ndarray): Asymmetrical lower values of errors for corresponding x value (length of x_errors_low has to be the same as x and y) |
| 40 | + x_errors_high(None or 1D numpy.ndarray): Asymmetrical upper values of errors for corresponding x value (length of x_errors_high has to be the same as x and y) |
| 41 | + y_errors_low(None or 1D numpy.ndarray): Asymmetrical lower values of errors for corresponding y value (length of y_errors_low has to be the same as x and y) |
| 42 | + y_errors_high(None or 1D numpy.ndarray): Asymmetrical upper values of errors for corresponding y value (length of y_errors_high has to be the same as x and y) |
| 43 | + title (str): Title of the histogram. |
| 44 | + xAxisLabel (str): Label of the X axis. |
| 45 | + yAxisLabel (str): Label of the Y axis. |
| 46 | + minY (None or float): Minimum value on the Y axis to be shown, if set to None then minY=min(y) |
| 47 | + maxY (None or float): Maximum value on the Y axis to be shown, if set to None then maxY=max(y) |
| 48 | + lineColor (int): Line color. (https://root.cern.ch/doc/master/classTAttLine.html) |
| 49 | + lineStyle (int): Line style. |
| 50 | + lineWidth (int): Line width. |
| 51 | + fillColor (int): Fill area color. (https://root.cern.ch/doc/master/classTAttFill.html) |
| 52 | + fillStyle (int): Fill area style. |
| 53 | + markerColor (int): Marker color. (https://root.cern.ch/doc/master/classTAttMarker.html) |
| 54 | + markerStyle (int): Marker style. |
| 55 | + markerSize (float): Marker size. |
| 56 | +
|
| 57 | + WARNING! This function only works for TGraph, because serialization of TGraphErrors and TGraphAsymmErrors is not implemented yet. |
| 58 | +
|
| 59 | + Function that converts arguments into TGraph, TGraphErrors or TGraphAsymmErros based on the given arguments. |
| 60 | + When all errors are unspecified, detected object is TGraph. |
| 61 | + When x_errors, y_errors are specified, detected object is TGraphErrors. |
| 62 | + When x_errors_low, x_errors_high, y_errors_low, y_errors_high are specified, detected object is TGraphAsymmErrors. |
| 63 | + Note that both x_errors, y_errors need to be specified or set to None. |
| 64 | + The same rule applies to x_errors_low, x_errors_high, y_errors_low, y_errors_high. |
| 65 | + Also can't specify x_errors, y_errors and x_errors_low, x_errors_high, y_errors_low, y_errors_high at the same time. |
| 66 | + All rules are designed to remove any ambiguity. |
| 67 | + """ |
| 68 | + |
| 69 | + sym_errors = [x_errors, y_errors] |
| 70 | + sym_errors_bool = [err is not None for err in sym_errors] |
| 71 | + |
| 72 | + asym_errors = [x_errors_low, x_errors_high, y_errors_low, y_errors_high] |
| 73 | + asym_errors_bool = [err is not None for err in asym_errors] |
| 74 | + |
| 75 | + tgraph_type = "TGraph" |
| 76 | + |
| 77 | + # Detecting which type of TGraph to chose |
| 78 | + if any(sym_errors_bool): |
| 79 | + if not all(sym_errors_bool): |
| 80 | + raise ValueError("uproot.as_TGraph requires both x_errors and y_errors") |
| 81 | + if any(asym_errors_bool): |
| 82 | + raise ValueError( |
| 83 | + "uproot.as_TGraph can accept symmetrical errors OR asymmetrical errors, but not both" |
| 84 | + ) |
| 85 | + tgraph_type = "TGraphErrors" |
| 86 | + |
| 87 | + elif any(asym_errors_bool): |
| 88 | + if not all(asym_errors_bool): |
| 89 | + raise ValueError( |
| 90 | + "uproot.as_TGraph requires all of the following: x_errors_low, x_errors_high, y_errors_low, y_errors_high" |
| 91 | + ) |
| 92 | + if any(sym_errors_bool): |
| 93 | + raise ValueError( |
| 94 | + "uproot.as_TGraph can accept symmetrical errors OR asymmetrical errors, but not both" |
| 95 | + ) |
| 96 | + tgraph_type = "TGraphAsymmErrors" |
| 97 | + |
| 98 | + tobject = uproot.models.TObject.Model_TObject.empty() |
| 99 | + |
| 100 | + tnamed = uproot.models.TNamed.Model_TNamed.empty() |
| 101 | + tnamed._deeply_writable = True |
| 102 | + tnamed._bases.append(tobject) |
| 103 | + tnamed._members["fName"] = ( |
| 104 | + "" # Temporary name, will be overwritten by the writing process because Uproot's write syntax is ``file[name] = histogram`` |
| 105 | + ) |
| 106 | + # Constraint so user won't break TGraph naming |
| 107 | + if ";" in title or ";" in xAxisLabel or ";" in yAxisLabel: |
| 108 | + raise ValueError("title and xAxisLabel and yAxisLabel can't contain ';'!") |
| 109 | + fTitle = f"{title};{xAxisLabel};{yAxisLabel}" |
| 110 | + tnamed._members["fTitle"] = fTitle |
| 111 | + |
| 112 | + # setting line styling |
| 113 | + tattline = uproot.models.TAtt.Model_TAttLine_v2.empty() |
| 114 | + tattline._deeply_writable = True |
| 115 | + tattline._members["fLineColor"] = lineColor |
| 116 | + tattline._members["fLineStyle"] = lineStyle |
| 117 | + tattline._members["fLineWidth"] = lineWidth |
| 118 | + |
| 119 | + # setting filling styling, does not do anything to TGraph |
| 120 | + tattfill = uproot.models.TAtt.Model_TAttFill_v2.empty() |
| 121 | + tattfill._deeply_writable = True |
| 122 | + tattfill._members["fFillColor"] = fillColor |
| 123 | + tattfill._members["fFillStyle"] = fillStyle |
| 124 | + |
| 125 | + # setting marker styling, those are points on graph |
| 126 | + tattmarker = uproot.models.TAtt.Model_TAttMarker_v2.empty() |
| 127 | + tattmarker._deeply_writable = True |
| 128 | + tattmarker._members["fMarkerColor"] = markerColor |
| 129 | + tattmarker._members["fMarkerStyle"] = markerStyle |
| 130 | + tattmarker._members["fMarkerSize"] = markerSize |
| 131 | + |
| 132 | + if len(x) != len(y): |
| 133 | + raise ValueError("Arrays x and y must have the same length!") |
| 134 | + if len(x) == 0: |
| 135 | + raise ValueError("uproot.as_TGraph x and y arrays can't be empty") |
| 136 | + if len(x.shape) != 1: |
| 137 | + raise ValueError(f"x has to be 1D, but is {len(x.shape)}D!") |
| 138 | + if len(y.shape) != 1: |
| 139 | + raise ValueError(f"y has to be 1D, but is {len(y.shape)}D!") |
| 140 | + |
| 141 | + if minY is None: |
| 142 | + new_minY = np.min(x) |
| 143 | + elif not isinstance(minY, numbers.Real): |
| 144 | + raise ValueError( |
| 145 | + f"uproot.as_TGraph minY has to be None or a number, not {type(minY)}" |
| 146 | + ) |
| 147 | + else: |
| 148 | + new_minY = minY |
| 149 | + |
| 150 | + if maxY is None: |
| 151 | + new_maxY = np.max(x) |
| 152 | + elif not isinstance(maxY, numbers.Real): |
| 153 | + raise ValueError( |
| 154 | + f"uproot.as_TGraph minY has to be None or a number, not {type(maxY)}" |
| 155 | + ) |
| 156 | + else: |
| 157 | + new_maxY = maxY |
| 158 | + |
| 159 | + tGraph = uproot.models.TGraph.Model_TGraph_v4.empty() |
| 160 | + |
| 161 | + tGraph._bases.append(tnamed) |
| 162 | + tGraph._bases.append(tattline) |
| 163 | + tGraph._bases.append(tattfill) |
| 164 | + tGraph._bases.append(tattmarker) |
| 165 | + |
| 166 | + tGraph._members["fNpoints"] = len(x) |
| 167 | + tGraph._members["fX"] = x |
| 168 | + tGraph._members["fY"] = y |
| 169 | + tGraph._members["fMinimum"] = ( |
| 170 | + minY if minY is not None else new_minY - 0.1 * (new_maxY - new_minY) |
| 171 | + ) # by default graph line wont touch the edge of the chart |
| 172 | + tGraph._members["fMaximum"] = ( |
| 173 | + maxY if maxY is not None else new_maxY + 0.1 * (new_maxY - new_minY) |
| 174 | + ) # by default graph line wont touch the edge of the chart |
| 175 | + |
| 176 | + returned_TGraph = tGraph |
| 177 | + |
| 178 | + if tgraph_type == "TGraphErrors": |
| 179 | + if not (len(x_errors) == len(y_errors) == len(x)): |
| 180 | + raise ValueError( |
| 181 | + "Length of all error arrays has to be the same as length of arrays X and Y" |
| 182 | + ) |
| 183 | + tGraphErrors = uproot.models.TGraph.Model_TGraphErrors_v3.empty() |
| 184 | + tGraphErrors._bases.append(tGraph) |
| 185 | + tGraphErrors._members["fEX"] = x_errors |
| 186 | + tGraphErrors._members["fEY"] = y_errors |
| 187 | + |
| 188 | + returned_TGraph = tGraphErrors |
| 189 | + elif tgraph_type == "TGraphAsymmErrors": |
| 190 | + if not ( |
| 191 | + len(x_errors_low) |
| 192 | + == len(x_errors_high) |
| 193 | + == len(y_errors_low) |
| 194 | + == len(y_errors_high) |
| 195 | + == len(x) |
| 196 | + ): |
| 197 | + raise ValueError( |
| 198 | + "Length of errors all error arrays has to be the same as length of arrays X and Y" |
| 199 | + ) |
| 200 | + tGraphAsymmErrors = uproot.models.TGraph.Model_TGraphAsymmErrors_v3.empty() |
| 201 | + tGraphAsymmErrors._bases.append(tGraph) |
| 202 | + tGraphAsymmErrors._members["fEXlow"] = x_errors_low |
| 203 | + tGraphAsymmErrors._members["fEXhigh"] = x_errors_high |
| 204 | + tGraphAsymmErrors._members["fEYlow"] = y_errors_low |
| 205 | + tGraphAsymmErrors._members["fEYhigh"] = y_errors_high |
| 206 | + |
| 207 | + returned_TGraph = tGraphAsymmErrors |
| 208 | + |
| 209 | + return returned_TGraph |
| 210 | + |
| 211 | + |
| 212 | +def as_TGraph( |
| 213 | + df, |
| 214 | + title="", |
| 215 | + xAxisLabel="", |
| 216 | + yAxisLabel="", |
| 217 | + minY=None, |
| 218 | + maxY=None, |
| 219 | + lineColor: int = 602, |
| 220 | + lineStyle: int = 1, |
| 221 | + lineWidth: int = 1, |
| 222 | + markerColor: int = 1, |
| 223 | + markerStyle: int = 1, |
| 224 | + markerSize: float = 1.0, |
| 225 | +): |
| 226 | + """ |
| 227 | + Args: |
| 228 | + df (DataFrame or and dict like object): DataFrame object with column names as follows: |
| 229 | + x (float): x values of TGraph. |
| 230 | + y (float): y values of TGraph. |
| 231 | + x_errors (float or left unspecified): Symethrical error values for corresponding x value |
| 232 | + y_errors (float or left unspecified): Symethrical error values for corresponding y value |
| 233 | + x_errors_low (float or left unspecified): Asymmetrical lower error values for corresponding x value |
| 234 | + x_errors_high (float or left unspecified): Asymmetrical upper error values for corresponding x value |
| 235 | + y_errors_low (float or left unspecified): Asymmetrical lower error values for corresponding y value |
| 236 | + y_errors_high (float or left unspecified): Asymmetrical upper error values for corresponding y value |
| 237 | + (other column names will be ignored!) |
| 238 | + title (str): Title of the histogram. |
| 239 | + xAxisLabel (str): Label of the X axis. |
| 240 | + yAxisLabel (str): Label of the Y axis. |
| 241 | + minY (None or float): Minimum value on the Y axis to be shown, if set to None then minY=min(y) |
| 242 | + maxY (None or float): Maximum value on the Y axis to be shown, if set to None then maxY=max(y) |
| 243 | + lineColor (int): Line color. (https://root.cern.ch/doc/master/classTAttLine.html) |
| 244 | + lineStyle (int): Line style. |
| 245 | + lineWidth (int): Line width. |
| 246 | + markerColor (int): Marker color. (https://root.cern.ch/doc/master/classTAttMarker.html) |
| 247 | + markerStyle (int): Marker style. |
| 248 | + markerSize (float): Marker size. |
| 249 | +
|
| 250 | + WARNING! This function only works for TGraph, because serialization of TGraphErrors and TGraphAsymmErrors is not implemented yet. |
| 251 | +
|
| 252 | + Function that converts DataFrame into TGraph, TGraphErrors or TGraphAsymmErros based on the specified DataFrame columns. |
| 253 | + When all error columns are unspecified, detected object is TGraph. |
| 254 | + When x_errors, y_errors are specified, detected object is TGraphErrors. |
| 255 | + When x_errors_low, x_errors_high, y_errors_low, y_errors_high are specified, detected object is TGraphAsymmErrors. |
| 256 | + Note that both {x_errors, x_errors} need to be specified or set to None. |
| 257 | + The same rule applies {to x_errors_low, x_errors_high, x_errors_low, x_errors_high}. |
| 258 | + Also can't specify {x_errors, y_errors} and {x_errors_low, x_errors_high, y_errors_low, y_errors_high} at the same time. |
| 259 | + """ |
| 260 | + |
| 261 | + x = np.array(df["x"]) if df.get("x", None) is not None else None |
| 262 | + y = np.array(df["y"]) if df.get("y", None) is not None else None |
| 263 | + x_errors = ( |
| 264 | + np.array(df["x_errors"]) if df.get("x_errors", None) is not None else None |
| 265 | + ) |
| 266 | + y_errors = ( |
| 267 | + np.array(df["y_errors"]) if df.get("y_errors", None) is not None else None |
| 268 | + ) |
| 269 | + x_errors_low = ( |
| 270 | + np.array(df["x_errors_low"]) |
| 271 | + if df.get("x_errors_low", None) is not None |
| 272 | + else None |
| 273 | + ) |
| 274 | + x_errors_high = ( |
| 275 | + np.array(df["x_errors_high"]) |
| 276 | + if df.get("x_errors_high", None) is not None |
| 277 | + else None |
| 278 | + ) |
| 279 | + y_errors_low = ( |
| 280 | + np.array(df["y_errors_low"]) |
| 281 | + if df.get("y_errors_low", None) is not None |
| 282 | + else None |
| 283 | + ) |
| 284 | + y_errors_high = ( |
| 285 | + np.array(df["y_errors_high"]) |
| 286 | + if df.get("y_errors_high", None) is not None |
| 287 | + else None |
| 288 | + ) |
| 289 | + |
| 290 | + return _as_TGraph( |
| 291 | + x, |
| 292 | + y, |
| 293 | + x_errors, |
| 294 | + y_errors, |
| 295 | + x_errors_low, |
| 296 | + x_errors_high, |
| 297 | + y_errors_low, |
| 298 | + y_errors_high, |
| 299 | + title, |
| 300 | + xAxisLabel, |
| 301 | + yAxisLabel, |
| 302 | + minY, |
| 303 | + maxY, |
| 304 | + lineColor, |
| 305 | + lineStyle, |
| 306 | + lineWidth, |
| 307 | + 0, |
| 308 | + 1001, |
| 309 | + markerColor, |
| 310 | + markerStyle, |
| 311 | + markerSize, |
| 312 | + ) |
0 commit comments