diff options
author | sotech117 <michael_foiani@brown.edu> | 2025-07-31 17:27:24 -0400 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2025-07-31 17:27:24 -0400 |
commit | 5bf22fc7e3c392c8bd44315ca2d06d7dca7d084e (patch) | |
tree | 8dacb0f195df1c0788d36dd0064f6bbaa3143ede /venv/lib/python3.8/site-packages/plotly/shapeannotation.py | |
parent | b832d364da8c2efe09e3f75828caf73c50d01ce3 (diff) |
add code for analysis of data
Diffstat (limited to 'venv/lib/python3.8/site-packages/plotly/shapeannotation.py')
-rw-r--r-- | venv/lib/python3.8/site-packages/plotly/shapeannotation.py | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/venv/lib/python3.8/site-packages/plotly/shapeannotation.py b/venv/lib/python3.8/site-packages/plotly/shapeannotation.py new file mode 100644 index 0000000..a2323ed --- /dev/null +++ b/venv/lib/python3.8/site-packages/plotly/shapeannotation.py @@ -0,0 +1,246 @@ +# some functions defined here to avoid numpy import + + +def _mean(x): + if len(x) == 0: + raise ValueError("x must have positive length") + return float(sum(x)) / len(x) + + +def _argmin(x): + return sorted(enumerate(x), key=lambda t: t[1])[0][0] + + +def _argmax(x): + return sorted(enumerate(x), key=lambda t: t[1], reverse=True)[0][0] + + +def _df_anno(xanchor, yanchor, x, y): + """Default annotation parameters""" + return dict(xanchor=xanchor, yanchor=yanchor, x=x, y=y, showarrow=False) + + +def _add_inside_to_position(pos): + if not ("inside" in pos or "outside" in pos): + pos.add("inside") + return pos + + +def _prepare_position(position, prepend_inside=False): + if position is None: + position = "top right" + pos_str = position + position = set(position.split(" ")) + if prepend_inside: + position = _add_inside_to_position(position) + return position, pos_str + + +def annotation_params_for_line(shape_type, shape_args, position): + # all x0, x1, y0, y1 are used to place the annotation, that way it could + # work with a slanted line + # even with a slanted line, there are the horizontal and vertical + # conventions of placing a shape + x0 = shape_args["x0"] + x1 = shape_args["x1"] + y0 = shape_args["y0"] + y1 = shape_args["y1"] + X = [x0, x1] + Y = [y0, y1] + R = "right" + T = "top" + L = "left" + C = "center" + B = "bottom" + M = "middle" + aY = max(Y) + iY = min(Y) + eY = _mean(Y) + aaY = _argmax(Y) + aiY = _argmin(Y) + aX = max(X) + iX = min(X) + eX = _mean(X) + aaX = _argmax(X) + aiX = _argmin(X) + position, pos_str = _prepare_position(position) + if shape_type == "vline": + if position == set(["top", "left"]): + return _df_anno(R, T, X[aaY], aY) + if position == set(["top", "right"]): + return _df_anno(L, T, X[aaY], aY) + if position == set(["top"]): + return _df_anno(C, B, X[aaY], aY) + if position == set(["bottom", "left"]): + return _df_anno(R, B, X[aiY], iY) + if position == set(["bottom", "right"]): + return _df_anno(L, B, X[aiY], iY) + if position == set(["bottom"]): + return _df_anno(C, T, X[aiY], iY) + if position == set(["left"]): + return _df_anno(R, M, eX, eY) + if position == set(["right"]): + return _df_anno(L, M, eX, eY) + elif shape_type == "hline": + if position == set(["top", "left"]): + return _df_anno(L, B, iX, Y[aiX]) + if position == set(["top", "right"]): + return _df_anno(R, B, aX, Y[aaX]) + if position == set(["top"]): + return _df_anno(C, B, eX, eY) + if position == set(["bottom", "left"]): + return _df_anno(L, T, iX, Y[aiX]) + if position == set(["bottom", "right"]): + return _df_anno(R, T, aX, Y[aaX]) + if position == set(["bottom"]): + return _df_anno(C, T, eX, eY) + if position == set(["left"]): + return _df_anno(R, M, iX, Y[aiX]) + if position == set(["right"]): + return _df_anno(L, M, aX, Y[aaX]) + raise ValueError('Invalid annotation position "%s"' % (pos_str,)) + + +def annotation_params_for_rect(shape_type, shape_args, position): + x0 = shape_args["x0"] + x1 = shape_args["x1"] + y0 = shape_args["y0"] + y1 = shape_args["y1"] + + position, pos_str = _prepare_position(position, prepend_inside=True) + if position == set(["inside", "top", "left"]): + return _df_anno("left", "top", min([x0, x1]), max([y0, y1])) + if position == set(["inside", "top", "right"]): + return _df_anno("right", "top", max([x0, x1]), max([y0, y1])) + if position == set(["inside", "top"]): + return _df_anno("center", "top", _mean([x0, x1]), max([y0, y1])) + if position == set(["inside", "bottom", "left"]): + return _df_anno("left", "bottom", min([x0, x1]), min([y0, y1])) + if position == set(["inside", "bottom", "right"]): + return _df_anno("right", "bottom", max([x0, x1]), min([y0, y1])) + if position == set(["inside", "bottom"]): + return _df_anno("center", "bottom", _mean([x0, x1]), min([y0, y1])) + if position == set(["inside", "left"]): + return _df_anno("left", "middle", min([x0, x1]), _mean([y0, y1])) + if position == set(["inside", "right"]): + return _df_anno("right", "middle", max([x0, x1]), _mean([y0, y1])) + if position == set(["inside"]): + # TODO: Do we want this? + return _df_anno("center", "middle", _mean([x0, x1]), _mean([y0, y1])) + if position == set(["outside", "top", "left"]): + return _df_anno( + "right" if shape_type == "vrect" else "left", + "bottom" if shape_type == "hrect" else "top", + min([x0, x1]), + max([y0, y1]), + ) + if position == set(["outside", "top", "right"]): + return _df_anno( + "left" if shape_type == "vrect" else "right", + "bottom" if shape_type == "hrect" else "top", + max([x0, x1]), + max([y0, y1]), + ) + if position == set(["outside", "top"]): + return _df_anno("center", "bottom", _mean([x0, x1]), max([y0, y1])) + if position == set(["outside", "bottom", "left"]): + return _df_anno( + "right" if shape_type == "vrect" else "left", + "top" if shape_type == "hrect" else "bottom", + min([x0, x1]), + min([y0, y1]), + ) + if position == set(["outside", "bottom", "right"]): + return _df_anno( + "left" if shape_type == "vrect" else "right", + "top" if shape_type == "hrect" else "bottom", + max([x0, x1]), + min([y0, y1]), + ) + if position == set(["outside", "bottom"]): + return _df_anno("center", "top", _mean([x0, x1]), min([y0, y1])) + if position == set(["outside", "left"]): + return _df_anno("right", "middle", min([x0, x1]), _mean([y0, y1])) + if position == set(["outside", "right"]): + return _df_anno("left", "middle", max([x0, x1]), _mean([y0, y1])) + raise ValueError("Invalid annotation position %s" % (pos_str,)) + + +def axis_spanning_shape_annotation(annotation, shape_type, shape_args, kwargs): + """ + annotation: a go.layout.Annotation object, a dict describing an annotation, or None + shape_type: one of 'vline', 'hline', 'vrect', 'hrect' and determines how the + x, y, xanchor, and yanchor values are set. + shape_args: the parameters used to draw the shape, which are used to place the annotation + kwargs: a dictionary that was the kwargs of a + _process_multiple_axis_spanning_shapes spanning shapes call. Items in this + dict whose keys start with 'annotation_' will be extracted and the keys with + the 'annotation_' part stripped off will be used to assign properties of the + new annotation. + + Property precedence: + The annotation's x, y, xanchor, and yanchor properties are set based on the + shape_type argument. Each property already specified in the annotation or + through kwargs will be left as is (not replaced by the value computed using + shape_type). Note that the xref and yref properties will in general get + overwritten if the result of this function is passed to an add_annotation + called with the row and col parameters specified. + + Returns an annotation populated with fields based on the + annotation_position, annotation_ prefixed kwargs or the original annotation + passed in to this function. + """ + # set properties based on annotation_ prefixed kwargs + prefix = "annotation_" + len_prefix = len(prefix) + annotation_keys = list(filter(lambda k: k.startswith(prefix), kwargs.keys())) + # If no annotation or annotation-key is specified, return None as we don't + # want an annotation in this case + if annotation is None and len(annotation_keys) == 0: + return None + # TODO: Would it be better if annotation were initialized to an instance of + # go.layout.Annotation ? + if annotation is None: + annotation = dict() + for k in annotation_keys: + if k == "annotation_position": + # don't set so that Annotation constructor doesn't complain + continue + subk = k[len_prefix:] + annotation[subk] = kwargs[k] + # set x, y, xanchor, yanchor based on shape_type and position + annotation_position = None + if "annotation_position" in kwargs.keys(): + annotation_position = kwargs["annotation_position"] + if shape_type.endswith("line"): + shape_dict = annotation_params_for_line( + shape_type, shape_args, annotation_position + ) + elif shape_type.endswith("rect"): + shape_dict = annotation_params_for_rect( + shape_type, shape_args, annotation_position + ) + for k in shape_dict.keys(): + # only set property derived from annotation_position if it hasn't already been set + # see above: this would be better as a go.layout.Annotation then the key + # would be checked for validity here (otherwise it is checked later, + # which I guess is ok too) + if (k not in annotation) or (annotation[k] is None): + annotation[k] = shape_dict[k] + return annotation + + +def split_dict_by_key_prefix(d, prefix): + """ + Returns two dictionaries, one containing all the items whose keys do not + start with a prefix and another containing all the items whose keys do start + with the prefix. Note that the prefix is not removed from the keys. + """ + no_prefix = dict() + with_prefix = dict() + for k in d.keys(): + if k.startswith(prefix): + with_prefix[k] = d[k] + else: + no_prefix[k] = d[k] + return (no_prefix, with_prefix) |