Mise à jour de Monitor.py et autres scripts
This commit is contained in:
696
myenv/lib/python3.11/site-packages/altair/__init__.py
Normal file
696
myenv/lib/python3.11/site-packages/altair/__init__.py
Normal file
@@ -0,0 +1,696 @@
|
||||
# ruff: noqa
|
||||
__version__ = "5.5.0"
|
||||
|
||||
# The content of __all__ is automatically written by
|
||||
# tools/update_init_file.py. Do not modify directly.
|
||||
__all__ = [
|
||||
"Aggregate",
|
||||
"AggregateOp",
|
||||
"AggregateTransform",
|
||||
"AggregatedFieldDef",
|
||||
"Align",
|
||||
"AllSortString",
|
||||
"AltairDeprecationWarning",
|
||||
"Angle",
|
||||
"AngleDatum",
|
||||
"AngleValue",
|
||||
"AnyMark",
|
||||
"AnyMarkConfig",
|
||||
"AreaConfig",
|
||||
"ArgmaxDef",
|
||||
"ArgminDef",
|
||||
"AutoSizeParams",
|
||||
"AutosizeType",
|
||||
"Axis",
|
||||
"AxisConfig",
|
||||
"AxisOrient",
|
||||
"AxisResolveMap",
|
||||
"BBox",
|
||||
"BarConfig",
|
||||
"BaseTitleNoValueRefs",
|
||||
"Baseline",
|
||||
"Bin",
|
||||
"BinExtent",
|
||||
"BinParams",
|
||||
"BinTransform",
|
||||
"BindCheckbox",
|
||||
"BindDirect",
|
||||
"BindInput",
|
||||
"BindRadioSelect",
|
||||
"BindRange",
|
||||
"Binding",
|
||||
"BinnedTimeUnit",
|
||||
"Blend",
|
||||
"BoxPlot",
|
||||
"BoxPlotConfig",
|
||||
"BoxPlotDef",
|
||||
"BrushConfig",
|
||||
"CalculateTransform",
|
||||
"Categorical",
|
||||
"ChainedWhen",
|
||||
"Chart",
|
||||
"ChartDataType",
|
||||
"Color",
|
||||
"ColorDatum",
|
||||
"ColorDef",
|
||||
"ColorName",
|
||||
"ColorScheme",
|
||||
"ColorValue",
|
||||
"Column",
|
||||
"CompositeMark",
|
||||
"CompositeMarkDef",
|
||||
"CompositionConfig",
|
||||
"ConcatChart",
|
||||
"ConcatSpecGenericSpec",
|
||||
"ConditionalAxisColor",
|
||||
"ConditionalAxisLabelAlign",
|
||||
"ConditionalAxisLabelBaseline",
|
||||
"ConditionalAxisLabelFontStyle",
|
||||
"ConditionalAxisLabelFontWeight",
|
||||
"ConditionalAxisNumber",
|
||||
"ConditionalAxisNumberArray",
|
||||
"ConditionalAxisPropertyAlignnull",
|
||||
"ConditionalAxisPropertyColornull",
|
||||
"ConditionalAxisPropertyFontStylenull",
|
||||
"ConditionalAxisPropertyFontWeightnull",
|
||||
"ConditionalAxisPropertyTextBaselinenull",
|
||||
"ConditionalAxisPropertynumberArraynull",
|
||||
"ConditionalAxisPropertynumbernull",
|
||||
"ConditionalAxisPropertystringnull",
|
||||
"ConditionalAxisString",
|
||||
"ConditionalMarkPropFieldOrDatumDef",
|
||||
"ConditionalMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDef",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterStringFieldDef",
|
||||
"ConditionalParameterValueDefGradientstringnullExprRef",
|
||||
"ConditionalParameterValueDefTextExprRef",
|
||||
"ConditionalParameterValueDefnumber",
|
||||
"ConditionalParameterValueDefnumberArrayExprRef",
|
||||
"ConditionalParameterValueDefnumberExprRef",
|
||||
"ConditionalParameterValueDefstringExprRef",
|
||||
"ConditionalParameterValueDefstringnullExprRef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalPredicateStringFieldDef",
|
||||
"ConditionalPredicateValueDefAlignnullExprRef",
|
||||
"ConditionalPredicateValueDefColornullExprRef",
|
||||
"ConditionalPredicateValueDefFontStylenullExprRef",
|
||||
"ConditionalPredicateValueDefFontWeightnullExprRef",
|
||||
"ConditionalPredicateValueDefGradientstringnullExprRef",
|
||||
"ConditionalPredicateValueDefTextBaselinenullExprRef",
|
||||
"ConditionalPredicateValueDefTextExprRef",
|
||||
"ConditionalPredicateValueDefnumber",
|
||||
"ConditionalPredicateValueDefnumberArrayExprRef",
|
||||
"ConditionalPredicateValueDefnumberArraynullExprRef",
|
||||
"ConditionalPredicateValueDefnumberExprRef",
|
||||
"ConditionalPredicateValueDefnumbernullExprRef",
|
||||
"ConditionalPredicateValueDefstringExprRef",
|
||||
"ConditionalPredicateValueDefstringnullExprRef",
|
||||
"ConditionalStringFieldDef",
|
||||
"ConditionalValueDefGradientstringnullExprRef",
|
||||
"ConditionalValueDefTextExprRef",
|
||||
"ConditionalValueDefnumber",
|
||||
"ConditionalValueDefnumberArrayExprRef",
|
||||
"ConditionalValueDefnumberExprRef",
|
||||
"ConditionalValueDefstringExprRef",
|
||||
"ConditionalValueDefstringnullExprRef",
|
||||
"Config",
|
||||
"CsvDataFormat",
|
||||
"Cursor",
|
||||
"Cyclical",
|
||||
"Data",
|
||||
"DataFormat",
|
||||
"DataSource",
|
||||
"DataType",
|
||||
"Datasets",
|
||||
"DateTime",
|
||||
"DatumChannelMixin",
|
||||
"DatumDef",
|
||||
"Day",
|
||||
"DensityTransform",
|
||||
"DerivedStream",
|
||||
"Description",
|
||||
"DescriptionValue",
|
||||
"Detail",
|
||||
"Dict",
|
||||
"DictInlineDataset",
|
||||
"DictSelectionInit",
|
||||
"DictSelectionInitInterval",
|
||||
"Diverging",
|
||||
"DomainUnionWith",
|
||||
"DsvDataFormat",
|
||||
"Element",
|
||||
"Encoding",
|
||||
"EncodingSortField",
|
||||
"ErrorBand",
|
||||
"ErrorBandConfig",
|
||||
"ErrorBandDef",
|
||||
"ErrorBar",
|
||||
"ErrorBarConfig",
|
||||
"ErrorBarDef",
|
||||
"ErrorBarExtent",
|
||||
"EventStream",
|
||||
"EventType",
|
||||
"Expr",
|
||||
"ExprRef",
|
||||
"ExtentTransform",
|
||||
"Facet",
|
||||
"FacetChart",
|
||||
"FacetEncodingFieldDef",
|
||||
"FacetFieldDef",
|
||||
"FacetMapping",
|
||||
"FacetSpec",
|
||||
"FacetedEncoding",
|
||||
"FacetedUnitSpec",
|
||||
"Feature",
|
||||
"FeatureCollection",
|
||||
"FeatureGeometryGeoJsonProperties",
|
||||
"Field",
|
||||
"FieldChannelMixin",
|
||||
"FieldDefWithoutScale",
|
||||
"FieldEqualPredicate",
|
||||
"FieldGTEPredicate",
|
||||
"FieldGTPredicate",
|
||||
"FieldLTEPredicate",
|
||||
"FieldLTPredicate",
|
||||
"FieldName",
|
||||
"FieldOneOfPredicate",
|
||||
"FieldOrDatumDefWithConditionDatumDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumber",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionDatumDefstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefTypeForShapestringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumber",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionStringDatumDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefstring",
|
||||
"FieldRange",
|
||||
"FieldRangePredicate",
|
||||
"FieldValidPredicate",
|
||||
"Fill",
|
||||
"FillDatum",
|
||||
"FillOpacity",
|
||||
"FillOpacityDatum",
|
||||
"FillOpacityValue",
|
||||
"FillValue",
|
||||
"FilterTransform",
|
||||
"Fit",
|
||||
"FlattenTransform",
|
||||
"FoldTransform",
|
||||
"FontStyle",
|
||||
"FontWeight",
|
||||
"FormatConfig",
|
||||
"Generator",
|
||||
"GenericUnitSpecEncodingAnyMark",
|
||||
"GeoJsonFeature",
|
||||
"GeoJsonFeatureCollection",
|
||||
"GeoJsonProperties",
|
||||
"Geometry",
|
||||
"GeometryCollection",
|
||||
"Gradient",
|
||||
"GradientStop",
|
||||
"GraticuleGenerator",
|
||||
"GraticuleParams",
|
||||
"HConcatChart",
|
||||
"HConcatSpecGenericSpec",
|
||||
"Header",
|
||||
"HeaderConfig",
|
||||
"HexColor",
|
||||
"Href",
|
||||
"HrefValue",
|
||||
"Impute",
|
||||
"ImputeMethod",
|
||||
"ImputeParams",
|
||||
"ImputeSequence",
|
||||
"ImputeTransform",
|
||||
"InlineData",
|
||||
"InlineDataset",
|
||||
"Interpolate",
|
||||
"IntervalSelectionConfig",
|
||||
"IntervalSelectionConfigWithoutType",
|
||||
"JoinAggregateFieldDef",
|
||||
"JoinAggregateTransform",
|
||||
"JsonDataFormat",
|
||||
"JupyterChart",
|
||||
"Key",
|
||||
"LabelOverlap",
|
||||
"LatLongDef",
|
||||
"LatLongFieldDef",
|
||||
"Latitude",
|
||||
"Latitude2",
|
||||
"Latitude2Datum",
|
||||
"Latitude2Value",
|
||||
"LatitudeDatum",
|
||||
"LayerChart",
|
||||
"LayerRepeatMapping",
|
||||
"LayerRepeatSpec",
|
||||
"LayerSpec",
|
||||
"LayoutAlign",
|
||||
"Legend",
|
||||
"LegendBinding",
|
||||
"LegendConfig",
|
||||
"LegendOrient",
|
||||
"LegendResolveMap",
|
||||
"LegendStreamBinding",
|
||||
"LineConfig",
|
||||
"LineString",
|
||||
"LinearGradient",
|
||||
"LocalMultiTimeUnit",
|
||||
"LocalSingleTimeUnit",
|
||||
"Locale",
|
||||
"LoessTransform",
|
||||
"LogicalAndPredicate",
|
||||
"LogicalNotPredicate",
|
||||
"LogicalOrPredicate",
|
||||
"Longitude",
|
||||
"Longitude2",
|
||||
"Longitude2Datum",
|
||||
"Longitude2Value",
|
||||
"LongitudeDatum",
|
||||
"LookupData",
|
||||
"LookupSelection",
|
||||
"LookupTransform",
|
||||
"Mark",
|
||||
"MarkConfig",
|
||||
"MarkDef",
|
||||
"MarkInvalidDataMode",
|
||||
"MarkPropDefGradientstringnull",
|
||||
"MarkPropDefnumber",
|
||||
"MarkPropDefnumberArray",
|
||||
"MarkPropDefstringnullTypeForShape",
|
||||
"MarkType",
|
||||
"MaxRowsError",
|
||||
"MergedStream",
|
||||
"Month",
|
||||
"MultiLineString",
|
||||
"MultiPoint",
|
||||
"MultiPolygon",
|
||||
"MultiTimeUnit",
|
||||
"NamedData",
|
||||
"NonArgAggregateOp",
|
||||
"NonLayerRepeatSpec",
|
||||
"NonNormalizedSpec",
|
||||
"NumberLocale",
|
||||
"NumericArrayMarkPropDef",
|
||||
"NumericMarkPropDef",
|
||||
"OffsetDef",
|
||||
"Opacity",
|
||||
"OpacityDatum",
|
||||
"OpacityValue",
|
||||
"Order",
|
||||
"OrderFieldDef",
|
||||
"OrderOnlyDef",
|
||||
"OrderValue",
|
||||
"OrderValueDef",
|
||||
"Orient",
|
||||
"Orientation",
|
||||
"OverlayMarkDef",
|
||||
"Padding",
|
||||
"Parameter",
|
||||
"ParameterExpression",
|
||||
"ParameterExtent",
|
||||
"ParameterName",
|
||||
"ParameterPredicate",
|
||||
"Parse",
|
||||
"ParseValue",
|
||||
"PivotTransform",
|
||||
"Point",
|
||||
"PointSelectionConfig",
|
||||
"PointSelectionConfigWithoutType",
|
||||
"PolarDef",
|
||||
"Polygon",
|
||||
"Position",
|
||||
"Position2Def",
|
||||
"PositionDatumDef",
|
||||
"PositionDatumDefBase",
|
||||
"PositionDef",
|
||||
"PositionFieldDef",
|
||||
"PositionFieldDefBase",
|
||||
"PositionValueDef",
|
||||
"Predicate",
|
||||
"PredicateComposition",
|
||||
"PrimitiveValue",
|
||||
"Projection",
|
||||
"ProjectionConfig",
|
||||
"ProjectionType",
|
||||
"QuantileTransform",
|
||||
"RadialGradient",
|
||||
"Radius",
|
||||
"Radius2",
|
||||
"Radius2Datum",
|
||||
"Radius2Value",
|
||||
"RadiusDatum",
|
||||
"RadiusValue",
|
||||
"RangeConfig",
|
||||
"RangeEnum",
|
||||
"RangeRaw",
|
||||
"RangeRawArray",
|
||||
"RangeScheme",
|
||||
"RectConfig",
|
||||
"RegressionTransform",
|
||||
"RelativeBandSize",
|
||||
"RepeatChart",
|
||||
"RepeatMapping",
|
||||
"RepeatRef",
|
||||
"RepeatSpec",
|
||||
"Resolve",
|
||||
"ResolveMode",
|
||||
"Root",
|
||||
"Row",
|
||||
"RowColLayoutAlign",
|
||||
"RowColboolean",
|
||||
"RowColnumber",
|
||||
"RowColumnEncodingFieldDef",
|
||||
"SCHEMA_URL",
|
||||
"SCHEMA_VERSION",
|
||||
"SampleTransform",
|
||||
"Scale",
|
||||
"ScaleBinParams",
|
||||
"ScaleBins",
|
||||
"ScaleConfig",
|
||||
"ScaleDatumDef",
|
||||
"ScaleFieldDef",
|
||||
"ScaleInterpolateEnum",
|
||||
"ScaleInterpolateParams",
|
||||
"ScaleInvalidDataConfig",
|
||||
"ScaleInvalidDataShowAsValueangle",
|
||||
"ScaleInvalidDataShowAsValuecolor",
|
||||
"ScaleInvalidDataShowAsValuefill",
|
||||
"ScaleInvalidDataShowAsValuefillOpacity",
|
||||
"ScaleInvalidDataShowAsValueopacity",
|
||||
"ScaleInvalidDataShowAsValueradius",
|
||||
"ScaleInvalidDataShowAsValueshape",
|
||||
"ScaleInvalidDataShowAsValuesize",
|
||||
"ScaleInvalidDataShowAsValuestroke",
|
||||
"ScaleInvalidDataShowAsValuestrokeDash",
|
||||
"ScaleInvalidDataShowAsValuestrokeOpacity",
|
||||
"ScaleInvalidDataShowAsValuestrokeWidth",
|
||||
"ScaleInvalidDataShowAsValuetheta",
|
||||
"ScaleInvalidDataShowAsValuex",
|
||||
"ScaleInvalidDataShowAsValuexOffset",
|
||||
"ScaleInvalidDataShowAsValuey",
|
||||
"ScaleInvalidDataShowAsValueyOffset",
|
||||
"ScaleInvalidDataShowAsangle",
|
||||
"ScaleInvalidDataShowAscolor",
|
||||
"ScaleInvalidDataShowAsfill",
|
||||
"ScaleInvalidDataShowAsfillOpacity",
|
||||
"ScaleInvalidDataShowAsopacity",
|
||||
"ScaleInvalidDataShowAsradius",
|
||||
"ScaleInvalidDataShowAsshape",
|
||||
"ScaleInvalidDataShowAssize",
|
||||
"ScaleInvalidDataShowAsstroke",
|
||||
"ScaleInvalidDataShowAsstrokeDash",
|
||||
"ScaleInvalidDataShowAsstrokeOpacity",
|
||||
"ScaleInvalidDataShowAsstrokeWidth",
|
||||
"ScaleInvalidDataShowAstheta",
|
||||
"ScaleInvalidDataShowAsx",
|
||||
"ScaleInvalidDataShowAsxOffset",
|
||||
"ScaleInvalidDataShowAsy",
|
||||
"ScaleInvalidDataShowAsyOffset",
|
||||
"ScaleResolveMap",
|
||||
"ScaleType",
|
||||
"SchemaBase",
|
||||
"SchemeParams",
|
||||
"SecondaryFieldDef",
|
||||
"SelectionConfig",
|
||||
"SelectionExpression",
|
||||
"SelectionInit",
|
||||
"SelectionInitInterval",
|
||||
"SelectionInitIntervalMapping",
|
||||
"SelectionInitMapping",
|
||||
"SelectionParameter",
|
||||
"SelectionPredicateComposition",
|
||||
"SelectionResolution",
|
||||
"SelectionType",
|
||||
"SequenceGenerator",
|
||||
"SequenceParams",
|
||||
"SequentialMultiHue",
|
||||
"SequentialSingleHue",
|
||||
"Shape",
|
||||
"ShapeDatum",
|
||||
"ShapeDef",
|
||||
"ShapeValue",
|
||||
"SharedEncoding",
|
||||
"SingleDefUnitChannel",
|
||||
"SingleTimeUnit",
|
||||
"Size",
|
||||
"SizeDatum",
|
||||
"SizeValue",
|
||||
"Sort",
|
||||
"SortArray",
|
||||
"SortByChannel",
|
||||
"SortByChannelDesc",
|
||||
"SortByEncoding",
|
||||
"SortField",
|
||||
"SortOrder",
|
||||
"Spec",
|
||||
"SphereGenerator",
|
||||
"StackOffset",
|
||||
"StackTransform",
|
||||
"StandardType",
|
||||
"Step",
|
||||
"StepFor",
|
||||
"Stream",
|
||||
"StringFieldDef",
|
||||
"StringFieldDefWithCondition",
|
||||
"StringValueDefWithCondition",
|
||||
"Stroke",
|
||||
"StrokeCap",
|
||||
"StrokeDash",
|
||||
"StrokeDashDatum",
|
||||
"StrokeDashValue",
|
||||
"StrokeDatum",
|
||||
"StrokeJoin",
|
||||
"StrokeOpacity",
|
||||
"StrokeOpacityDatum",
|
||||
"StrokeOpacityValue",
|
||||
"StrokeValue",
|
||||
"StrokeWidth",
|
||||
"StrokeWidthDatum",
|
||||
"StrokeWidthValue",
|
||||
"StyleConfigIndex",
|
||||
"SymbolShape",
|
||||
"TOPLEVEL_ONLY_KEYS",
|
||||
"Text",
|
||||
"TextBaseline",
|
||||
"TextDatum",
|
||||
"TextDef",
|
||||
"TextDirection",
|
||||
"TextValue",
|
||||
"Then",
|
||||
"Theta",
|
||||
"Theta2",
|
||||
"Theta2Datum",
|
||||
"Theta2Value",
|
||||
"ThetaDatum",
|
||||
"ThetaValue",
|
||||
"TickConfig",
|
||||
"TickCount",
|
||||
"TimeInterval",
|
||||
"TimeIntervalStep",
|
||||
"TimeLocale",
|
||||
"TimeUnit",
|
||||
"TimeUnitParams",
|
||||
"TimeUnitTransform",
|
||||
"TimeUnitTransformParams",
|
||||
"Title",
|
||||
"TitleAnchor",
|
||||
"TitleConfig",
|
||||
"TitleFrame",
|
||||
"TitleOrient",
|
||||
"TitleParams",
|
||||
"Tooltip",
|
||||
"TooltipContent",
|
||||
"TooltipValue",
|
||||
"TopLevelConcatSpec",
|
||||
"TopLevelFacetSpec",
|
||||
"TopLevelHConcatSpec",
|
||||
"TopLevelLayerSpec",
|
||||
"TopLevelMixin",
|
||||
"TopLevelParameter",
|
||||
"TopLevelRepeatSpec",
|
||||
"TopLevelSelectionParameter",
|
||||
"TopLevelSpec",
|
||||
"TopLevelUnitSpec",
|
||||
"TopLevelVConcatSpec",
|
||||
"TopoDataFormat",
|
||||
"Transform",
|
||||
"Type",
|
||||
"TypeForShape",
|
||||
"TypedFieldDef",
|
||||
"URI",
|
||||
"Undefined",
|
||||
"UnitSpec",
|
||||
"UnitSpecWithFrame",
|
||||
"Url",
|
||||
"UrlData",
|
||||
"UrlValue",
|
||||
"UtcMultiTimeUnit",
|
||||
"UtcSingleTimeUnit",
|
||||
"VConcatChart",
|
||||
"VConcatSpecGenericSpec",
|
||||
"VEGAEMBED_VERSION",
|
||||
"VEGALITE_VERSION",
|
||||
"VEGA_VERSION",
|
||||
"ValueChannelMixin",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefGradientstringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefTypeForShapestringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumber",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumberArray",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefstringnull",
|
||||
"ValueDefWithConditionStringFieldDefText",
|
||||
"ValueDefnumber",
|
||||
"ValueDefnumberwidthheightExprRef",
|
||||
"VariableParameter",
|
||||
"Vector10string",
|
||||
"Vector12string",
|
||||
"Vector2DateTime",
|
||||
"Vector2Vector2number",
|
||||
"Vector2boolean",
|
||||
"Vector2number",
|
||||
"Vector2string",
|
||||
"Vector3number",
|
||||
"Vector7string",
|
||||
"VegaLite",
|
||||
"VegaLiteSchema",
|
||||
"ViewBackground",
|
||||
"ViewConfig",
|
||||
"When",
|
||||
"WindowEventType",
|
||||
"WindowFieldDef",
|
||||
"WindowOnlyOp",
|
||||
"WindowTransform",
|
||||
"X",
|
||||
"X2",
|
||||
"X2Datum",
|
||||
"X2Value",
|
||||
"XDatum",
|
||||
"XError",
|
||||
"XError2",
|
||||
"XError2Value",
|
||||
"XErrorValue",
|
||||
"XOffset",
|
||||
"XOffsetDatum",
|
||||
"XOffsetValue",
|
||||
"XValue",
|
||||
"Y",
|
||||
"Y2",
|
||||
"Y2Datum",
|
||||
"Y2Value",
|
||||
"YDatum",
|
||||
"YError",
|
||||
"YError2",
|
||||
"YError2Value",
|
||||
"YErrorValue",
|
||||
"YOffset",
|
||||
"YOffsetDatum",
|
||||
"YOffsetValue",
|
||||
"YValue",
|
||||
"api",
|
||||
"binding",
|
||||
"binding_checkbox",
|
||||
"binding_radio",
|
||||
"binding_range",
|
||||
"binding_select",
|
||||
"channels",
|
||||
"check_fields_and_encodings",
|
||||
"compiler",
|
||||
"concat",
|
||||
"condition",
|
||||
"core",
|
||||
"data",
|
||||
"data_transformers",
|
||||
"datum",
|
||||
"default_data_transformer",
|
||||
"display",
|
||||
"expr",
|
||||
"graticule",
|
||||
"hconcat",
|
||||
"jupyter",
|
||||
"layer",
|
||||
"limit_rows",
|
||||
"load_ipython_extension",
|
||||
"load_schema",
|
||||
"mixins",
|
||||
"param",
|
||||
"parse_shorthand",
|
||||
"renderers",
|
||||
"repeat",
|
||||
"sample",
|
||||
"schema",
|
||||
"selection_interval",
|
||||
"selection_point",
|
||||
"sequence",
|
||||
"sphere",
|
||||
"theme",
|
||||
"to_csv",
|
||||
"to_json",
|
||||
"to_values",
|
||||
"topo_feature",
|
||||
"typing",
|
||||
"utils",
|
||||
"v5",
|
||||
"value",
|
||||
"vconcat",
|
||||
"vegalite",
|
||||
"vegalite_compilers",
|
||||
"when",
|
||||
"with_property_setters",
|
||||
]
|
||||
|
||||
|
||||
def __dir__():
|
||||
return __all__
|
||||
|
||||
|
||||
from altair.vegalite import *
|
||||
from altair.vegalite.v5.schema.core import Dict
|
||||
from altair.jupyter import JupyterChart
|
||||
from altair.expr import expr
|
||||
from altair.utils import AltairDeprecationWarning, parse_shorthand, Undefined
|
||||
from altair import typing, theme
|
||||
|
||||
|
||||
def load_ipython_extension(ipython):
|
||||
from altair._magics import vegalite
|
||||
|
||||
ipython.register_magic_function(vegalite, "cell")
|
||||
|
||||
|
||||
def __getattr__(name: str):
|
||||
from altair.utils.deprecation import deprecated_warn
|
||||
|
||||
if name == "themes":
|
||||
deprecated_warn(
|
||||
"Most cases require only the following change:\n\n"
|
||||
" # Deprecated\n"
|
||||
" alt.themes.enable('quartz')\n\n"
|
||||
" # Updated\n"
|
||||
" alt.theme.enable('quartz')\n\n"
|
||||
"If your code registers a theme, make the following change:\n\n"
|
||||
" # Deprecated\n"
|
||||
" def custom_theme():\n"
|
||||
" return {'height': 400, 'width': 700}\n"
|
||||
" alt.themes.register('theme_name', custom_theme)\n"
|
||||
" alt.themes.enable('theme_name')\n\n"
|
||||
" # Updated\n"
|
||||
" @alt.theme.register('theme_name', enable=True)\n"
|
||||
" def custom_theme():\n"
|
||||
" return alt.theme.ThemeConfig(\n"
|
||||
" {'height': 400, 'width': 700}\n"
|
||||
" )\n\n"
|
||||
"See the updated User Guide for further details:\n"
|
||||
" https://altair-viz.github.io/user_guide/api.html#theme\n"
|
||||
" https://altair-viz.github.io/user_guide/customization.html#chart-themes",
|
||||
version="5.5.0",
|
||||
alternative="altair.theme",
|
||||
stacklevel=3,
|
||||
action="once",
|
||||
)
|
||||
return theme._themes
|
||||
else:
|
||||
msg = f"module {__name__!r} has no attribute {name!r}"
|
||||
raise AttributeError(msg)
|
||||
109
myenv/lib/python3.11/site-packages/altair/_magics.py
Normal file
109
myenv/lib/python3.11/site-packages/altair/_magics.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""Magic functions for rendering vega-lite specifications."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import warnings
|
||||
from importlib.util import find_spec
|
||||
from typing import Any
|
||||
|
||||
from IPython.core import magic_arguments
|
||||
from narwhals.stable.v1.dependencies import is_pandas_dataframe
|
||||
|
||||
from altair.vegalite import v5 as vegalite_v5
|
||||
|
||||
__all__ = ["vegalite"]
|
||||
|
||||
RENDERERS = {
|
||||
"vega-lite": {
|
||||
"5": vegalite_v5.VegaLite,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
TRANSFORMERS = {
|
||||
"vega-lite": {
|
||||
"5": vegalite_v5.data_transformers,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _prepare_data(data, data_transformers):
|
||||
"""Convert input data to data for use within schema."""
|
||||
if data is None or isinstance(data, dict):
|
||||
return data
|
||||
elif is_pandas_dataframe(data):
|
||||
if func := data_transformers.get():
|
||||
data = func(data)
|
||||
return data
|
||||
elif isinstance(data, str):
|
||||
return {"url": data}
|
||||
else:
|
||||
warnings.warn(f"data of type {type(data)} not recognized", stacklevel=1)
|
||||
return data
|
||||
|
||||
|
||||
def _get_variable(name: str) -> Any:
|
||||
"""Get a variable from the notebook namespace."""
|
||||
from IPython.core.getipython import get_ipython
|
||||
|
||||
if ip := get_ipython():
|
||||
if name not in ip.user_ns:
|
||||
msg = f"argument '{name}' does not match the name of any defined variable"
|
||||
raise NameError(msg)
|
||||
return ip.user_ns[name]
|
||||
else:
|
||||
msg = (
|
||||
"Magic command must be run within an IPython "
|
||||
"environment, in which get_ipython() is defined."
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@magic_arguments.magic_arguments()
|
||||
@magic_arguments.argument(
|
||||
"data",
|
||||
nargs="?",
|
||||
help="local variablename of a pandas DataFrame to be used as the dataset",
|
||||
)
|
||||
@magic_arguments.argument("-v", "--version", dest="version", default="v5")
|
||||
@magic_arguments.argument("-j", "--json", dest="json", action="store_true")
|
||||
def vegalite(line, cell) -> vegalite_v5.VegaLite:
|
||||
"""
|
||||
Cell magic for displaying vega-lite visualizations in CoLab.
|
||||
|
||||
%%vegalite [dataframe] [--json] [--version='v5']
|
||||
|
||||
Visualize the contents of the cell using Vega-Lite, optionally
|
||||
specifying a pandas DataFrame object to be used as the dataset.
|
||||
|
||||
if --json is passed, then input is parsed as json rather than yaml.
|
||||
"""
|
||||
args = magic_arguments.parse_argstring(vegalite, line)
|
||||
existing_versions = {"v5": "5"}
|
||||
version = existing_versions[args.version]
|
||||
assert version in RENDERERS["vega-lite"]
|
||||
VegaLite = RENDERERS["vega-lite"][version]
|
||||
data_transformers = TRANSFORMERS["vega-lite"][version]
|
||||
|
||||
if args.json:
|
||||
spec = json.loads(cell)
|
||||
elif not find_spec("yaml"):
|
||||
try:
|
||||
spec = json.loads(cell)
|
||||
except json.JSONDecodeError as err:
|
||||
msg = (
|
||||
"%%vegalite: spec is not valid JSON. "
|
||||
"Install pyyaml to parse spec as yaml"
|
||||
)
|
||||
raise ValueError(msg) from err
|
||||
else:
|
||||
import yaml
|
||||
|
||||
spec = yaml.load(cell, Loader=yaml.SafeLoader)
|
||||
|
||||
if args.data is not None:
|
||||
data = _get_variable(args.data)
|
||||
spec["data"] = _prepare_data(data, data_transformers)
|
||||
|
||||
return VegaLite(spec)
|
||||
2034
myenv/lib/python3.11/site-packages/altair/expr/__init__.py
Normal file
2034
myenv/lib/python3.11/site-packages/altair/expr/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
13
myenv/lib/python3.11/site-packages/altair/expr/consts.py
Normal file
13
myenv/lib/python3.11/site-packages/altair/expr/consts.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
CONST_LISTING = {
|
||||
"NaN": "not a number (same as JavaScript literal NaN)",
|
||||
"LN10": "the natural log of 10 (alias to Math.LN10)",
|
||||
"E": "the transcendental number e (alias to Math.E)",
|
||||
"LOG10E": "the base 10 logarithm e (alias to Math.LOG10E)",
|
||||
"LOG2E": "the base 2 logarithm of e (alias to Math.LOG2E)",
|
||||
"SQRT1_2": "the square root of 0.5 (alias to Math.SQRT1_2)",
|
||||
"LN2": "the natural log of 2 (alias to Math.LN2)",
|
||||
"SQRT2": "the square root of 2 (alias to Math.SQRT1_2)",
|
||||
"PI": "the transcendental number pi (alias to Math.PI)",
|
||||
}
|
||||
282
myenv/lib/python3.11/site-packages/altair/expr/core.py
Normal file
282
myenv/lib/python3.11/site-packages/altair/expr/core.py
Normal file
@@ -0,0 +1,282 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
from typing import TYPE_CHECKING, Any, Literal, Union
|
||||
|
||||
from altair.utils import SchemaBase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
|
||||
from altair.vegalite.v5.schema._typing import Map, PrimitiveValue_T
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
|
||||
class DatumType:
|
||||
"""An object to assist in building Vega-Lite Expressions."""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "datum"
|
||||
|
||||
def __getattr__(self, attr) -> GetAttrExpression:
|
||||
if attr.startswith("__") and attr.endswith("__"):
|
||||
raise AttributeError(attr)
|
||||
return GetAttrExpression("datum", attr)
|
||||
|
||||
def __getitem__(self, attr) -> GetItemExpression:
|
||||
return GetItemExpression("datum", attr)
|
||||
|
||||
def __call__(self, datum, **kwargs) -> dict[str, Any]:
|
||||
"""Specify a datum for use in an encoding."""
|
||||
return dict(datum=datum, **kwargs)
|
||||
|
||||
|
||||
datum = DatumType()
|
||||
|
||||
|
||||
def _js_repr(val) -> str:
|
||||
"""Return a javascript-safe string representation of val."""
|
||||
if val is True:
|
||||
return "true"
|
||||
elif val is False:
|
||||
return "false"
|
||||
elif val is None:
|
||||
return "null"
|
||||
elif isinstance(val, OperatorMixin):
|
||||
return val._to_expr()
|
||||
elif isinstance(val, dt.date):
|
||||
return _from_date_datetime(val)
|
||||
else:
|
||||
return repr(val)
|
||||
|
||||
|
||||
def _from_date_datetime(obj: dt.date | dt.datetime, /) -> str:
|
||||
"""
|
||||
Parse native `datetime.(date|datetime)` into a `datetime expression`_ string.
|
||||
|
||||
**Month is 0-based**
|
||||
|
||||
.. _datetime expression:
|
||||
https://vega.github.io/vega/docs/expressions/#datetime
|
||||
"""
|
||||
fn_name: Literal["datetime", "utc"] = "datetime"
|
||||
args: tuple[int, ...] = obj.year, obj.month - 1, obj.day
|
||||
if isinstance(obj, dt.datetime):
|
||||
if tzinfo := obj.tzinfo:
|
||||
if tzinfo is dt.timezone.utc:
|
||||
fn_name = "utc"
|
||||
else:
|
||||
msg = (
|
||||
f"Unsupported timezone {tzinfo!r}.\n"
|
||||
"Only `'UTC'` or naive (local) datetimes are permitted.\n"
|
||||
"See https://altair-viz.github.io/user_guide/generated/core/altair.DateTime.html"
|
||||
)
|
||||
raise TypeError(msg)
|
||||
us = obj.microsecond
|
||||
ms = us if us == 0 else us // 1_000
|
||||
args = *args, obj.hour, obj.minute, obj.second, ms
|
||||
return FunctionExpression(fn_name, args)._to_expr()
|
||||
|
||||
|
||||
# Designed to work with Expression and VariableParameter
|
||||
class OperatorMixin:
|
||||
def _to_expr(self) -> str:
|
||||
return repr(self)
|
||||
|
||||
def _from_expr(self, expr) -> Any:
|
||||
return expr
|
||||
|
||||
def __add__(self, other):
|
||||
comp_value = BinaryExpression("+", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __radd__(self, other):
|
||||
comp_value = BinaryExpression("+", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __sub__(self, other):
|
||||
comp_value = BinaryExpression("-", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rsub__(self, other):
|
||||
comp_value = BinaryExpression("-", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __mul__(self, other):
|
||||
comp_value = BinaryExpression("*", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rmul__(self, other):
|
||||
comp_value = BinaryExpression("*", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __truediv__(self, other):
|
||||
comp_value = BinaryExpression("/", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rtruediv__(self, other):
|
||||
comp_value = BinaryExpression("/", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
__div__ = __truediv__
|
||||
|
||||
__rdiv__ = __rtruediv__
|
||||
|
||||
def __mod__(self, other):
|
||||
comp_value = BinaryExpression("%", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rmod__(self, other):
|
||||
comp_value = BinaryExpression("%", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __pow__(self, other):
|
||||
# "**" Javascript operator is not supported in all browsers
|
||||
comp_value = FunctionExpression("pow", (self, other))
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rpow__(self, other):
|
||||
# "**" Javascript operator is not supported in all browsers
|
||||
comp_value = FunctionExpression("pow", (other, self))
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __neg__(self):
|
||||
comp_value = UnaryExpression("-", self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __pos__(self):
|
||||
comp_value = UnaryExpression("+", self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
# comparison operators
|
||||
|
||||
def __eq__(self, other):
|
||||
comp_value = BinaryExpression("===", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __ne__(self, other):
|
||||
comp_value = BinaryExpression("!==", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __gt__(self, other):
|
||||
comp_value = BinaryExpression(">", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __lt__(self, other):
|
||||
comp_value = BinaryExpression("<", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __ge__(self, other):
|
||||
comp_value = BinaryExpression(">=", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __le__(self, other):
|
||||
comp_value = BinaryExpression("<=", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __abs__(self):
|
||||
comp_value = FunctionExpression("abs", (self,))
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
# logical operators
|
||||
|
||||
def __and__(self, other):
|
||||
comp_value = BinaryExpression("&&", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __rand__(self, other):
|
||||
comp_value = BinaryExpression("&&", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __or__(self, other):
|
||||
comp_value = BinaryExpression("||", self, other)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __ror__(self, other):
|
||||
comp_value = BinaryExpression("||", other, self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
def __invert__(self):
|
||||
comp_value = UnaryExpression("!", self)
|
||||
return self._from_expr(comp_value)
|
||||
|
||||
|
||||
class Expression(OperatorMixin, SchemaBase):
|
||||
"""
|
||||
Expression.
|
||||
|
||||
Base object for enabling build-up of Javascript expressions using
|
||||
a Python syntax. Calling ``repr(obj)`` will return a Javascript
|
||||
representation of the object and the operations it encodes.
|
||||
"""
|
||||
|
||||
_schema = {"type": "string"}
|
||||
|
||||
def to_dict(self, *args, **kwargs):
|
||||
return repr(self)
|
||||
|
||||
def __setattr__(self, attr, val) -> None:
|
||||
# We don't need the setattr magic defined in SchemaBase
|
||||
return object.__setattr__(self, attr, val)
|
||||
|
||||
# item access
|
||||
def __getitem__(self, val):
|
||||
return GetItemExpression(self, val)
|
||||
|
||||
|
||||
class UnaryExpression(Expression):
|
||||
def __init__(self, op, val) -> None:
|
||||
super().__init__(op=op, val=val)
|
||||
|
||||
def __repr__(self):
|
||||
return f"({self.op}{_js_repr(self.val)})"
|
||||
|
||||
|
||||
class BinaryExpression(Expression):
|
||||
def __init__(self, op, lhs, rhs) -> None:
|
||||
super().__init__(op=op, lhs=lhs, rhs=rhs)
|
||||
|
||||
def __repr__(self):
|
||||
return f"({_js_repr(self.lhs)} {self.op} {_js_repr(self.rhs)})"
|
||||
|
||||
|
||||
class FunctionExpression(Expression):
|
||||
def __init__(self, name, args) -> None:
|
||||
super().__init__(name=name, args=args)
|
||||
|
||||
def __repr__(self):
|
||||
args = ",".join(_js_repr(arg) for arg in self.args)
|
||||
return f"{self.name}({args})"
|
||||
|
||||
|
||||
class ConstExpression(Expression):
|
||||
def __init__(self, name) -> None:
|
||||
super().__init__(name=name)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self.name)
|
||||
|
||||
|
||||
class GetAttrExpression(Expression):
|
||||
def __init__(self, group, name) -> None:
|
||||
super().__init__(group=group, name=name)
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.group}.{self.name}"
|
||||
|
||||
|
||||
class GetItemExpression(Expression):
|
||||
def __init__(self, group, name) -> None:
|
||||
super().__init__(group=group, name=name)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.group}[{self.name!r}]"
|
||||
|
||||
|
||||
IntoExpression: TypeAlias = Union[
|
||||
"PrimitiveValue_T", dt.date, dt.datetime, OperatorMixin, "Map"
|
||||
]
|
||||
167
myenv/lib/python3.11/site-packages/altair/expr/funcs.py
Normal file
167
myenv/lib/python3.11/site-packages/altair/expr/funcs.py
Normal file
@@ -0,0 +1,167 @@
|
||||
from __future__ import annotations
|
||||
|
||||
FUNCTION_LISTING = {
|
||||
"isArray": r"Returns true if _value_ is an array, false otherwise.",
|
||||
"isBoolean": r"Returns true if _value_ is a boolean (`true` or `false`), false otherwise.",
|
||||
"isDate": r"Returns true if _value_ is a Date object, false otherwise. This method will return false for timestamp numbers or date-formatted strings; it recognizes Date objects only.",
|
||||
"isDefined": r"Returns true if _value_ is a defined value, false if _value_ equals `undefined`. This method will return true for `null` and `NaN` values.",
|
||||
"isNumber": r"Returns true if _value_ is a number, false otherwise. `NaN` and `Infinity` are considered numbers.",
|
||||
"isObject": r"Returns true if _value_ is an object (including arrays and Dates), false otherwise.",
|
||||
"isRegExp": r"Returns true if _value_ is a RegExp (regular expression) object, false otherwise.",
|
||||
"isString": r"Returns true if _value_ is a string, false otherwise.",
|
||||
"isValid": r"Returns true if _value_ is not `null`, `undefined`, or `NaN`, false otherwise.",
|
||||
"toBoolean": r"Coerces the input _value_ to a string. Null values and empty strings are mapped to `null`.",
|
||||
"toDate": r"Coerces the input _value_ to a Date instance. Null values and empty strings are mapped to `null`. If an optional _parser_ function is provided, it is used to perform date parsing, otherwise `Date.parse` is used. Be aware that `Date.parse` has different implementations across browsers!",
|
||||
"toNumber": r"Coerces the input _value_ to a number. Null values and empty strings are mapped to `null`.",
|
||||
"toString": r"Coerces the input _value_ to a string. Null values and empty strings are mapped to `null`.",
|
||||
"if": r"If _test_ is truthy, returns _thenValue_. Otherwise, returns _elseValue_. The _if_ function is equivalent to the ternary operator `a ? b : c`.",
|
||||
"isNaN": r"Returns true if _value_ is not a number. Same as JavaScript's `isNaN`.",
|
||||
"isFinite": r"Returns true if _value_ is a finite number. Same as JavaScript's `isFinite`.",
|
||||
"abs": r"Returns the absolute value of _value_. Same as JavaScript's `Math.abs`.",
|
||||
"acos": r"Trigonometric arccosine. Same as JavaScript's `Math.acos`.",
|
||||
"asin": r"Trigonometric arcsine. Same as JavaScript's `Math.asin`.",
|
||||
"atan": r"Trigonometric arctangent. Same as JavaScript's `Math.atan`.",
|
||||
"atan2": r"Returns the arctangent of _dy / dx_. Same as JavaScript's `Math.atan2`.",
|
||||
"ceil": r"Rounds _value_ to the nearest integer of equal or greater value. Same as JavaScript's `Math.ceil`.",
|
||||
"clamp": r"Restricts _value_ to be between the specified _min_ and _max_.",
|
||||
"cos": r"Trigonometric cosine. Same as JavaScript's `Math.cos`.",
|
||||
"exp": r"Returns the value of _e_ raised to the provided _exponent_. Same as JavaScript's `Math.exp`.",
|
||||
"floor": r"Rounds _value_ to the nearest integer of equal or lower value. Same as JavaScript's `Math.floor`.",
|
||||
"hypot": r"Returns the square root of the sum of squares of its arguments. Same as JavaScript's `Math.hypot`.",
|
||||
"log": r"Returns the natural logarithm of _value_. Same as JavaScript's `Math.log`.",
|
||||
"max": r"Returns the maximum argument value. Same as JavaScript's `Math.max`.",
|
||||
"min": r"Returns the minimum argument value. Same as JavaScript's `Math.min`.",
|
||||
"pow": r"Returns _value_ raised to the given _exponent_. Same as JavaScript's `Math.pow`.",
|
||||
"random": r"Returns a pseudo-random number in the range [0,1). Same as JavaScript's `Math.random`.",
|
||||
"round": r"Rounds _value_ to the nearest integer. Same as JavaScript's `Math.round`.",
|
||||
"sin": r"Trigonometric sine. Same as JavaScript's `Math.sin`.",
|
||||
"sqrt": r"Square root function. Same as JavaScript's `Math.sqrt`.",
|
||||
"tan": r"Trigonometric tangent. Same as JavaScript's `Math.tan`.",
|
||||
"sampleNormal": r"Returns a sample from a univariate [normal (Gaussian) probability distribution](https://en.wikipedia.org/wiki/Normal_distribution) with specified _mean_ and standard deviation _stdev_. If unspecified, the mean defaults to `0` and the standard deviation defaults to `1`.",
|
||||
"cumulativeNormal": r"Returns the value of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) at the given input domain _value_ for a normal distribution with specified _mean_ and standard deviation _stdev_. If unspecified, the mean defaults to `0` and the standard deviation defaults to `1`.",
|
||||
"densityNormal": r"Returns the value of the [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) at the given input domain _value_, for a normal distribution with specified _mean_ and standard deviation _stdev_. If unspecified, the mean defaults to `0` and the standard deviation defaults to `1`.",
|
||||
"quantileNormal": r"Returns the quantile value (the inverse of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)) for the given input _probability_, for a normal distribution with specified _mean_ and standard deviation _stdev_. If unspecified, the mean defaults to `0` and the standard deviation defaults to `1`.",
|
||||
"sampleLogNormal": r"Returns a sample from a univariate [log-normal probability distribution](https://en.wikipedia.org/wiki/Log-normal_distribution) with specified log _mean_ and log standard deviation _stdev_. If unspecified, the log mean defaults to `0` and the log standard deviation defaults to `1`.",
|
||||
"cumulativeLogNormal": r"Returns the value of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) at the given input domain _value_ for a log-normal distribution with specified log _mean_ and log standard deviation _stdev_. If unspecified, the log mean defaults to `0` and the log standard deviation defaults to `1`.",
|
||||
"densityLogNormal": r"Returns the value of the [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) at the given input domain _value_, for a log-normal distribution with specified log _mean_ and log standard deviation _stdev_. If unspecified, the log mean defaults to `0` and the log standard deviation defaults to `1`.",
|
||||
"quantileLogNormal": r"Returns the quantile value (the inverse of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)) for the given input _probability_, for a log-normal distribution with specified log _mean_ and log standard deviation _stdev_. If unspecified, the log mean defaults to `0` and the log standard deviation defaults to `1`.",
|
||||
"sampleUniform": r"Returns a sample from a univariate [continuous uniform probability distribution](https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)) over the interval [_min_, _max_). If unspecified, _min_ defaults to `0` and _max_ defaults to `1`. If only one argument is provided, it is interpreted as the _max_ value.",
|
||||
"cumulativeUniform": r"Returns the value of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function) at the given input domain _value_ for a uniform distribution over the interval [_min_, _max_). If unspecified, _min_ defaults to `0` and _max_ defaults to `1`. If only one argument is provided, it is interpreted as the _max_ value.",
|
||||
"densityUniform": r"Returns the value of the [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) at the given input domain _value_, for a uniform distribution over the interval [_min_, _max_). If unspecified, _min_ defaults to `0` and _max_ defaults to `1`. If only one argument is provided, it is interpreted as the _max_ value.",
|
||||
"quantileUniform": r"Returns the quantile value (the inverse of the [cumulative distribution function](https://en.wikipedia.org/wiki/Cumulative_distribution_function)) for the given input _probability_, for a uniform distribution over the interval [_min_, _max_). If unspecified, _min_ defaults to `0` and _max_ defaults to `1`. If only one argument is provided, it is interpreted as the _max_ value.",
|
||||
"now": r"Returns the timestamp for the current time.",
|
||||
"datetime": r"Returns a new `Date` instance. The _month_ is 0-based, such that `1` represents February.",
|
||||
"date": r"Returns the day of the month for the given _datetime_ value, in local time.",
|
||||
"day": r"Returns the day of the week for the given _datetime_ value, in local time.",
|
||||
"dayofyear": r"Returns the one-based day of the year for the given _datetime_ value, in local time.",
|
||||
"year": r"Returns the year for the given _datetime_ value, in local time.",
|
||||
"quarter": r"Returns the quarter of the year (0-3) for the given _datetime_ value, in local time.",
|
||||
"month": r"Returns the (zero-based) month for the given _datetime_ value, in local time.",
|
||||
"week": r"Returns the week number of the year for the given _datetime_, in local time. This function assumes Sunday-based weeks. Days before the first Sunday of the year are considered to be in week 0, the first Sunday of the year is the start of week 1, the second Sunday week 2, _etc._.",
|
||||
"hours": r"Returns the hours component for the given _datetime_ value, in local time.",
|
||||
"minutes": r"Returns the minutes component for the given _datetime_ value, in local time.",
|
||||
"seconds": r"Returns the seconds component for the given _datetime_ value, in local time.",
|
||||
"milliseconds": r"Returns the milliseconds component for the given _datetime_ value, in local time.",
|
||||
"time": r"Returns the epoch-based timestamp for the given _datetime_ value.",
|
||||
"timezoneoffset": r"Returns the timezone offset from the local timezone to UTC for the given _datetime_ value.",
|
||||
"timeOffset": r"Returns a new `Date` instance that offsets the given _date_ by the specified time [_unit_](../api/time/#time-units) in the local timezone. The optional _step_ argument indicates the number of time unit steps to offset by (default 1).",
|
||||
"timeSequence": r"Returns an array of `Date` instances from _start_ (inclusive) to _stop_ (exclusive), with each entry separated by the given time [_unit_](../api/time/#time-units) in the local timezone. The optional _step_ argument indicates the number of time unit steps to take between each sequence entry (default 1).",
|
||||
"utc": r"Returns a timestamp for the given UTC date. The _month_ is 0-based, such that `1` represents February.",
|
||||
"utcdate": r"Returns the day of the month for the given _datetime_ value, in UTC time.",
|
||||
"utcday": r"Returns the day of the week for the given _datetime_ value, in UTC time.",
|
||||
"utcdayofyear": r"Returns the one-based day of the year for the given _datetime_ value, in UTC time.",
|
||||
"utcyear": r"Returns the year for the given _datetime_ value, in UTC time.",
|
||||
"utcquarter": r"Returns the quarter of the year (0-3) for the given _datetime_ value, in UTC time.",
|
||||
"utcmonth": r"Returns the (zero-based) month for the given _datetime_ value, in UTC time.",
|
||||
"utcweek": r"Returns the week number of the year for the given _datetime_, in UTC time. This function assumes Sunday-based weeks. Days before the first Sunday of the year are considered to be in week 0, the first Sunday of the year is the start of week 1, the second Sunday week 2, _etc._.",
|
||||
"utchours": r"Returns the hours component for the given _datetime_ value, in UTC time.",
|
||||
"utcminutes": r"Returns the minutes component for the given _datetime_ value, in UTC time.",
|
||||
"utcseconds": r"Returns the seconds component for the given _datetime_ value, in UTC time.",
|
||||
"utcmilliseconds": r"Returns the milliseconds component for the given _datetime_ value, in UTC time.",
|
||||
"utcOffset": r"Returns a new `Date` instance that offsets the given _date_ by the specified time [_unit_](../api/time/#time-units) in UTC time. The optional _step_ argument indicates the number of time unit steps to offset by (default 1).",
|
||||
"utcSequence": r"Returns an array of `Date` instances from _start_ (inclusive) to _stop_ (exclusive), with each entry separated by the given time [_unit_](../api/time/#time-units) in UTC time. The optional _step_ argument indicates the number of time unit steps to take between each sequence entry (default 1).",
|
||||
"extent": r"Returns a new _[min, max]_ array with the minimum and maximum values of the input array, ignoring `null`, `undefined`, and `NaN` values.",
|
||||
"clampRange": r"Clamps a two-element _range_ array in a span-preserving manner. If the span of the input _range_ is less than _(max - min)_ and an endpoint exceeds either the _min_ or _max_ value, the range is translated such that the span is preserved and one endpoint touches the boundary of the _[min, max]_ range. If the span exceeds _(max - min)_, the range _[min, max]_ is returned.",
|
||||
"indexof": r"Returns the first index of _value_ in the input _array_, or the first index of _substring_ in the input _string_..",
|
||||
"inrange": r"Tests whether _value_ lies within (or is equal to either) the first and last values of the _range_ array.",
|
||||
"join": r"Returns a new string by concatenating all of the elements of the input _array_, separated by commas or a specified _separator_ string.",
|
||||
"lastindexof": r"Returns the last index of _value_ in the input _array_, or the last index of _substring_ in the input _string_..",
|
||||
"length": r"Returns the length of the input _array_, or the length of the input _string_.",
|
||||
"lerp": r"Returns the linearly interpolated value between the first and last entries in the _array_ for the provided interpolation _fraction_ (typically between 0 and 1). For example, `lerp([0, 50], 0.5)` returns 25.",
|
||||
"peek": r"Returns the last element in the input _array_. Similar to the built-in `Array.pop` method, except that it does not remove the last element. This method is a convenient shorthand for `array[array.length - 1]`.",
|
||||
"pluck": r"Retrieves the value for the specified *field* from a given *array* of objects. The input *field* string may include nested properties (e.g., `foo.bar.bz`).",
|
||||
"reverse": r"Returns a new array with elements in a reverse order of the input _array_. The first array element becomes the last, and the last array element becomes the first.",
|
||||
"sequence": r"Returns an array containing an arithmetic sequence of numbers. If _step_ is omitted, it defaults to 1. If _start_ is omitted, it defaults to 0. The _stop_ value is exclusive; it is not included in the result. If _step_ is positive, the last element is the largest _start + i * step_ less than _stop_; if _step_ is negative, the last element is the smallest _start + i * step_ greater than _stop_. If the returned array would contain an infinite number of values, an empty range is returned. The arguments are not required to be integers.",
|
||||
"slice": r"Returns a section of _array_ between the _start_ and _end_ indices. If the _end_ argument is negative, it is treated as an offset from the end of the array (_length(array) + end_).",
|
||||
"span": r"Returns the span of _array_: the difference between the last and first elements, or _array[array.length-1] - array[0]_. Or if input is a string: a section of _string_ between the _start_ and _end_ indices. If the _end_ argument is negative, it is treated as an offset from the end of the string (_length(string) + end_)..",
|
||||
"lower": r"Transforms _string_ to lower-case letters.",
|
||||
"pad": r"Pads a _string_ value with repeated instances of a _character_ up to a specified _length_. If _character_ is not specified, a space (' ') is used. By default, padding is added to the end of a string. An optional _align_ parameter specifies if padding should be added to the `'left'` (beginning), `'center'`, or `'right'` (end) of the input string.",
|
||||
"parseFloat": r"Parses the input _string_ to a floating-point value. Same as JavaScript's `parseFloat`.",
|
||||
"parseInt": r"Parses the input _string_ to an integer value. Same as JavaScript's `parseInt`.",
|
||||
"replace": r"Returns a new string with some or all matches of _pattern_ replaced by a _replacement_ string. The _pattern_ can be a string or a regular expression. If _pattern_ is a string, only the first instance will be replaced. Same as [JavaScript's String.replace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace).",
|
||||
"split": r"Returns an array of tokens created by splitting the input _string_ according to a provided _separator_ pattern. The result can optionally be constrained to return at most _limit_ tokens.",
|
||||
"substring": r"Returns a section of _string_ between the _start_ and _end_ indices.",
|
||||
"trim": r"Returns a trimmed string with preceding and trailing whitespace removed.",
|
||||
"truncate": r"Truncates an input _string_ to a target _length_. The optional _align_ argument indicates what part of the string should be truncated: `'left'` (the beginning), `'center'`, or `'right'` (the end). By default, the `'right'` end of the string is truncated. The optional _ellipsis_ argument indicates the string to use to indicate truncated content; by default the ellipsis character `...` (`\\u2026`) is used.",
|
||||
"upper": r"Transforms _string_ to upper-case letters.",
|
||||
"merge": r"Merges the input objects _object1_, _object2_, etc into a new output object. Inputs are visited in sequential order, such that key values from later arguments can overwrite those from earlier arguments. Example: `merge({a:1, b:2}, {a:3}) -> {a:3, b:2}`.",
|
||||
"dayFormat": r"Formats a (0-6) _weekday_ number as a full week day name, according to the current locale. For example: `dayFormat(0) -> \"Sunday\"`.",
|
||||
"dayAbbrevFormat": r"Formats a (0-6) _weekday_ number as an abbreviated week day name, according to the current locale. For example: `dayAbbrevFormat(0) -> \"Sun\"`.",
|
||||
"format": r"Formats a numeric _value_ as a string. The _specifier_ must be a valid [d3-format specifier](https://github.com/d3/d3-format/) (e.g., `format(value, ',.2f')`.",
|
||||
"monthFormat": r"Formats a (zero-based) _month_ number as a full month name, according to the current locale. For example: `monthFormat(0) -> \"January\"`.",
|
||||
"monthAbbrevFormat": r"Formats a (zero-based) _month_ number as an abbreviated month name, according to the current locale. For example: `monthAbbrevFormat(0) -> \"Jan\"`.",
|
||||
"timeUnitSpecifier": r"Returns a time format specifier string for the given time [_units_](../api/time/#time-units). The optional _specifiers_ object provides a set of specifier sub-strings for customizing the format; for more, see the [timeUnitSpecifier API documentation](../api/time/#timeUnitSpecifier). The resulting specifier string can then be used as input to the [timeFormat](#timeFormat) or [utcFormat](#utcFormat) functions, or as the _format_ parameter of an axis or legend. For example: `timeFormat(date, timeUnitSpecifier('year'))` or `timeFormat(date, timeUnitSpecifier(['hours', 'minutes']))`.",
|
||||
"timeFormat": r"Formats a datetime _value_ (either a `Date` object or timestamp) as a string, according to the local time. The _specifier_ must be a valid [d3-time-format specifier](https://github.com/d3/d3-time-format/). For example: `timeFormat(timestamp, '%A')`.",
|
||||
"timeParse": r"Parses a _string_ value to a Date object, according to the local time. The _specifier_ must be a valid [d3-time-format specifier](https://github.com/d3/d3-time-format/). For example: `timeParse('June 30, 2015', '%B %d, %Y')`.",
|
||||
"utcFormat": r"Formats a datetime _value_ (either a `Date` object or timestamp) as a string, according to [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) time. The _specifier_ must be a valid [d3-time-format specifier](https://github.com/d3/d3-time-format/). For example: `utcFormat(timestamp, '%A')`.",
|
||||
"utcParse": r"Parses a _string_ value to a Date object, according to [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) time. The _specifier_ must be a valid [d3-time-format specifier](https://github.com/d3/d3-time-format/). For example: `utcParse('June 30, 2015', '%B %d, %Y')`.",
|
||||
"regexp": r"Creates a regular expression instance from an input _pattern_ string and optional _flags_. Same as [JavaScript's `RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp).",
|
||||
"test": r"Evaluates a regular expression _regexp_ against the input _string_, returning `true` if the string matches the pattern, `false` otherwise. For example: `test(/\\d{3}/, \"32-21-9483\") -> true`.",
|
||||
"rgb": r"Constructs a new [RGB](https://en.wikipedia.org/wiki/RGB_color_model) color. If _r_, _g_ and _b_ are specified, these represent the channel values of the returned color; an _opacity_ may also be specified. If a CSS Color Module Level 3 _specifier_ string is specified, it is parsed and then converted to the RGB color space. Uses [d3-color's rgb function](https://github.com/d3/d3-color#rgb).",
|
||||
"hsl": r"Constructs a new [HSL](https://en.wikipedia.org/wiki/HSL_and_HSV) color. If _h_, _s_ and _l_ are specified, these represent the channel values of the returned color; an _opacity_ may also be specified. If a CSS Color Module Level 3 _specifier_ string is specified, it is parsed and then converted to the HSL color space. Uses [d3-color's hsl function](https://github.com/d3/d3-color#hsl).",
|
||||
"lab": r"Constructs a new [CIE LAB](https://en.wikipedia.org/wiki/Lab_color_space#CIELAB) color. If _l_, _a_ and _b_ are specified, these represent the channel values of the returned color; an _opacity_ may also be specified. If a CSS Color Module Level 3 _specifier_ string is specified, it is parsed and then converted to the LAB color space. Uses [d3-color's lab function](https://github.com/d3/d3-color#lab).",
|
||||
"hcl": r"Constructs a new [HCL](https://en.wikipedia.org/wiki/Lab_color_space#CIELAB) (hue, chroma, luminance) color. If _h_, _c_ and _l_ are specified, these represent the channel values of the returned color; an _opacity_ may also be specified. If a CSS Color Module Level 3 _specifier_ string is specified, it is parsed and then converted to the HCL color space. Uses [d3-color's hcl function](https://github.com/d3/d3-color#hcl).",
|
||||
"luminance": r"Returns the luminance for the given color _specifier_ (compatible with [d3-color's rgb function](https://github.com/d3/d3-color#rgb)). The luminance is calculated according to the [W3C Web Content Accessibility Guidelines](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef).",
|
||||
"contrast": r"Returns the contrast ratio between the input color specifiers as a float between 1 and 21. The contrast is calculated according to the [W3C Web Content Accessibility Guidelines](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef).",
|
||||
"item": r"Returns the current scenegraph item that is the target of the event.",
|
||||
"group": r"Returns the scenegraph group mark item in which the current event has occurred. If no arguments are provided, the immediate parent group is returned. If a group name is provided, the matching ancestor group item is returned.",
|
||||
"xy": r"Returns the x- and y-coordinates for the current event as a two-element array. If no arguments are provided, the top-level coordinate space of the view is used. If a scenegraph _item_ (or string group name) is provided, the coordinate space of the group item is used.",
|
||||
"x": r"Returns the x coordinate for the current event. If no arguments are provided, the top-level coordinate space of the view is used. If a scenegraph _item_ (or string group name) is provided, the coordinate space of the group item is used.",
|
||||
"y": r"Returns the y coordinate for the current event. If no arguments are provided, the top-level coordinate space of the view is used. If a scenegraph _item_ (or string group name) is provided, the coordinate space of the group item is used.",
|
||||
"pinchDistance": r"Returns the pixel distance between the first two touch points of a multi-touch event.",
|
||||
"pinchAngle": r"Returns the angle of the line connecting the first two touch points of a multi-touch event.",
|
||||
"inScope": r"Returns true if the given scenegraph _item_ is a descendant of the group mark in which the event handler was defined, false otherwise.",
|
||||
"data": r"Returns the array of data objects for the Vega data set with the given _name_. If the data set is not found, returns an empty array.",
|
||||
"indata": r"Tests if the data set with a given _name_ contains a datum with a _field_ value that matches the input _value_. For example: `indata('table', 'category', value)`.",
|
||||
"scale": r"Applies the named scale transform (or projection) to the specified _value_. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale or projection.",
|
||||
"invert": r"Inverts the named scale transform (or projection) for the specified _value_. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale or projection.",
|
||||
"copy": r"Returns a copy (a new cloned instance) of the named scale transform of projection, or `undefined` if no scale or projection is found. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale or projection.",
|
||||
"domain": r"Returns the scale domain array for the named scale transform, or an empty array if the scale is not found. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale.",
|
||||
"range": r"Returns the scale range array for the named scale transform, or an empty array if the scale is not found. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale.",
|
||||
"bandwidth": r"Returns the current band width for the named band scale transform, or zero if the scale is not found or is not a band scale. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the scale.",
|
||||
"bandspace": r"Returns the number of steps needed within a band scale, based on the _count_ of domain elements and the inner and outer padding values. While normally calculated within the scale itself, this function can be helpful for determining the size of a chart's layout.",
|
||||
"gradient": r"Returns a linear color gradient for the _scale_ (whose range must be a [continuous color scheme](../schemes)) and starting and ending points _p0_ and _p1_, each an _[x, y]_ array. The points _p0_ and _p1_ should be expressed in normalized coordinates in the domain [0, 1], relative to the bounds of the item being colored. If unspecified, _p0_ defaults to `[0, 0]` and _p1_ defaults to `[1, 0]`, for a horizontal gradient that spans the full bounds of an item. The optional _count_ argument indicates a desired target number of sample points to take from the color scale.",
|
||||
"panLinear": r"Given a linear scale _domain_ array with numeric or datetime values, returns a new two-element domain array that is the result of panning the domain by a fractional _delta_. The _delta_ value represents fractional units of the scale range; for example, `0.5` indicates panning the scale domain to the right by half the scale range.",
|
||||
"panLog": r"Given a log scale _domain_ array with numeric or datetime values, returns a new two-element domain array that is the result of panning the domain by a fractional _delta_. The _delta_ value represents fractional units of the scale range; for example, `0.5` indicates panning the scale domain to the right by half the scale range.",
|
||||
"panPow": r"Given a power scale _domain_ array with numeric or datetime values and the given _exponent_, returns a new two-element domain array that is the result of panning the domain by a fractional _delta_. The _delta_ value represents fractional units of the scale range; for example, `0.5` indicates panning the scale domain to the right by half the scale range.",
|
||||
"panSymlog": r"Given a symmetric log scale _domain_ array with numeric or datetime values parameterized by the given _constant_, returns a new two-element domain array that is the result of panning the domain by a fractional _delta_. The _delta_ value represents fractional units of the scale range; for example, `0.5` indicates panning the scale domain to the right by half the scale range.",
|
||||
"zoomLinear": r"Given a linear scale _domain_ array with numeric or datetime values, returns a new two-element domain array that is the result of zooming the domain by a _scaleFactor_, centered at the provided fractional _anchor_. The _anchor_ value represents the zoom position in terms of fractional units of the scale range; for example, `0.5` indicates a zoom centered on the mid-point of the scale range.",
|
||||
"zoomLog": r"Given a log scale _domain_ array with numeric or datetime values, returns a new two-element domain array that is the result of zooming the domain by a _scaleFactor_, centered at the provided fractional _anchor_. The _anchor_ value represents the zoom position in terms of fractional units of the scale range; for example, `0.5` indicates a zoom centered on the mid-point of the scale range.",
|
||||
"zoomPow": r"Given a power scale _domain_ array with numeric or datetime values and the given _exponent_, returns a new two-element domain array that is the result of zooming the domain by a _scaleFactor_, centered at the provided fractional _anchor_. The _anchor_ value represents the zoom position in terms of fractional units of the scale range; for example, `0.5` indicates a zoom centered on the mid-point of the scale range.",
|
||||
"zoomSymlog": r"Given a symmetric log scale _domain_ array with numeric or datetime values parameterized by the given _constant_, returns a new two-element domain array that is the result of zooming the domain by a _scaleFactor_, centered at the provided fractional _anchor_. The _anchor_ value represents the zoom position in terms of fractional units of the scale range; for example, `0.5` indicates a zoom centered on the mid-point of the scale range.",
|
||||
"geoArea": r"Returns the projected planar area (typically in square pixels) of a GeoJSON _feature_ according to the named _projection_. If the _projection_ argument is `null`, computes the spherical area in steradians using unprojected longitude, latitude coordinates. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the projection. Uses d3-geo's [geoArea](https://github.com/d3/d3-geo#geoArea) and [path.area](https://github.com/d3/d3-geo#path_area) methods.",
|
||||
"geoBounds": r"Returns the projected planar bounding box (typically in pixels) for the specified GeoJSON _feature_, according to the named _projection_. The bounding box is represented by a two-dimensional array: [[_x0_, _y0_], [_x1_, _y1_]], where _x0_ is the minimum x-coordinate, _y0_ is the minimum y-coordinate, _x1_ is the maximum x-coordinate, and _y1_ is the maximum y-coordinate. If the _projection_ argument is `null`, computes the spherical bounding box using unprojected longitude, latitude coordinates. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the projection. Uses d3-geo's [geoBounds](https://github.com/d3/d3-geo#geoBounds) and [path.bounds](https://github.com/d3/d3-geo#path_bounds) methods.",
|
||||
"geoCentroid": r"Returns the projected planar centroid (typically in pixels) for the specified GeoJSON _feature_, according to the named _projection_. If the _projection_ argument is `null`, computes the spherical centroid using unprojected longitude, latitude coordinates. The optional _group_ argument takes a scenegraph group mark item to indicate the specific scope in which to look up the projection. Uses d3-geo's [geoCentroid](https://github.com/d3/d3-geo#geoCentroid) and [path.centroid](https://github.com/d3/d3-geo#path_centroid) methods.",
|
||||
"treePath": r"For the hierarchy data set with the given _name_, returns the shortest path through from the _source_ node id to the _target_ node id. The path starts at the _source_ node, ascends to the least common ancestor of the _source_ node and the _target_ node, and then descends to the _target_ node.",
|
||||
"treeAncestors": r"For the hierarchy data set with the given _name_, returns the array of ancestors nodes, starting with the input _node_, then followed by each parent up to the root.",
|
||||
"containerSize": r"Returns the current CSS box size (`[el.clientWidth, el.clientHeight]`) of the parent DOM element that contains the Vega view. If there is no container element, returns `[undefined, undefined]`.",
|
||||
"screen": r"Returns the [`window.screen`](https://developer.mozilla.org/en-US/docs/Web/API/Window/screen) object, or `{}` if Vega is not running in a browser environment.",
|
||||
"windowSize": r"Returns the current window size (`[window.innerWidth, window.innerHeight]`) or `[undefined, undefined]` if Vega is not running in a browser environment.",
|
||||
"warn": r"Logs a warning message and returns the last argument. For the message to appear in the console, the visualization view must have the appropriate logging level set.",
|
||||
"info": r"Logs an informative message and returns the last argument. For the message to appear in the console, the visualization view must have the appropriate logging level set.",
|
||||
"debug": r"Logs a debugging message and returns the last argument. For the message to appear in the console, the visualization view must have the appropriate logging level set.",
|
||||
}
|
||||
|
||||
|
||||
# This maps vega expression function names to the Python name
|
||||
NAME_MAP = {"if": "if_"}
|
||||
@@ -0,0 +1,21 @@
|
||||
try:
|
||||
import anywidget # noqa: F401
|
||||
except ImportError:
|
||||
# When anywidget isn't available, create stand-in JupyterChart class
|
||||
# that raises an informative import error on construction. This
|
||||
# way we can make JupyterChart available in the altair namespace
|
||||
# when anywidget is not installed
|
||||
class JupyterChart:
|
||||
def __init__(self, *args, **kwargs):
|
||||
msg = (
|
||||
"The Altair JupyterChart requires the anywidget \n"
|
||||
"Python package which may be installed using pip with\n"
|
||||
" pip install anywidget\n"
|
||||
"or using conda with\n"
|
||||
" conda install -c conda-forge anywidget\n"
|
||||
"Afterwards, you will need to restart your Python kernel."
|
||||
)
|
||||
raise ImportError(msg)
|
||||
|
||||
else:
|
||||
from .jupyter_chart import JupyterChart # noqa: F401
|
||||
@@ -0,0 +1,2 @@
|
||||
# JupyterChart
|
||||
This directory contains the JavaScript portion of the Altair `JupyterChart`. The `JupyterChart` is based on the [AnyWidget](https://anywidget.dev/) project.
|
||||
230
myenv/lib/python3.11/site-packages/altair/jupyter/js/index.js
Normal file
230
myenv/lib/python3.11/site-packages/altair/jupyter/js/index.js
Normal file
@@ -0,0 +1,230 @@
|
||||
import vegaEmbed from "https://esm.sh/vega-embed@6?deps=vega@5&deps=vega-lite@5.20.1";
|
||||
import lodashDebounce from "https://esm.sh/lodash-es@4.17.21/debounce";
|
||||
|
||||
// Note: For offline support, the import lines above are removed and the remaining script
|
||||
// is bundled using vl-convert's javascript_bundle function. See the documentation of
|
||||
// the javascript_bundle function for details on the available imports and their names.
|
||||
// If an additional import is required in the future, it will need to be added to vl-convert
|
||||
// in order to preserve offline support.
|
||||
async function render({ model, el }) {
|
||||
let finalize;
|
||||
|
||||
function showError(error){
|
||||
el.innerHTML = (
|
||||
'<div style="color:red;">'
|
||||
+ '<p>JavaScript Error: ' + error.message + '</p>'
|
||||
+ "<p>This usually means there's a typo in your chart specification. "
|
||||
+ "See the javascript console for the full traceback.</p>"
|
||||
+ '</div>'
|
||||
);
|
||||
}
|
||||
|
||||
const reembed = async () => {
|
||||
if (finalize != null) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
model.set("local_tz", Intl.DateTimeFormat().resolvedOptions().timeZone);
|
||||
|
||||
let spec = structuredClone(model.get("spec"));
|
||||
if (spec == null) {
|
||||
// Remove any existing chart and return
|
||||
while (el.firstChild) {
|
||||
el.removeChild(el.lastChild);
|
||||
}
|
||||
model.save_changes();
|
||||
return;
|
||||
}
|
||||
let embedOptions = structuredClone(model.get("embed_options")) ?? undefined;
|
||||
|
||||
let api;
|
||||
try {
|
||||
api = await vegaEmbed(el, spec, embedOptions);
|
||||
} catch (error) {
|
||||
showError(error)
|
||||
return;
|
||||
}
|
||||
|
||||
finalize = api.finalize;
|
||||
|
||||
// Debounce config
|
||||
const wait = model.get("debounce_wait") ?? 10;
|
||||
const debounceOpts = {leading: false, trailing: true};
|
||||
if (model.get("max_wait") ?? true) {
|
||||
debounceOpts["maxWait"] = wait;
|
||||
}
|
||||
|
||||
const initialSelections = {};
|
||||
for (const selectionName of Object.keys(model.get("_vl_selections"))) {
|
||||
const storeName = `${selectionName}_store`;
|
||||
const selectionHandler = (_, value) => {
|
||||
const newSelections = cleanJson(model.get("_vl_selections") ?? {});
|
||||
const store = cleanJson(api.view.data(storeName) ?? []);
|
||||
|
||||
newSelections[selectionName] = {value, store};
|
||||
model.set("_vl_selections", newSelections);
|
||||
model.save_changes();
|
||||
};
|
||||
api.view.addSignalListener(selectionName, lodashDebounce(selectionHandler, wait, debounceOpts));
|
||||
|
||||
initialSelections[selectionName] = {
|
||||
value: cleanJson(api.view.signal(selectionName) ?? {}),
|
||||
store: cleanJson(api.view.data(storeName) ?? [])
|
||||
}
|
||||
}
|
||||
model.set("_vl_selections", initialSelections);
|
||||
|
||||
const initialParams = {};
|
||||
for (const paramName of Object.keys(model.get("_params"))) {
|
||||
const paramHandler = (_, value) => {
|
||||
const newParams = JSON.parse(JSON.stringify(model.get("_params"))) || {};
|
||||
newParams[paramName] = value;
|
||||
model.set("_params", newParams);
|
||||
model.save_changes();
|
||||
};
|
||||
api.view.addSignalListener(paramName, lodashDebounce(paramHandler, wait, debounceOpts));
|
||||
|
||||
initialParams[paramName] = api.view.signal(paramName) ?? null
|
||||
}
|
||||
model.set("_params", initialParams);
|
||||
model.save_changes();
|
||||
|
||||
// Param change callback
|
||||
model.on('change:_params', async (new_params) => {
|
||||
for (const [param, value] of Object.entries(new_params.changed._params)) {
|
||||
api.view.signal(param, value);
|
||||
}
|
||||
await api.view.runAsync();
|
||||
});
|
||||
|
||||
// Add signal/data listeners
|
||||
for (const watch of model.get("_js_watch_plan") ?? []) {
|
||||
if (watch.namespace === "data") {
|
||||
const dataHandler = (_, value) => {
|
||||
model.set("_js_to_py_updates", [{
|
||||
namespace: "data",
|
||||
name: watch.name,
|
||||
scope: watch.scope,
|
||||
value: cleanJson(value)
|
||||
}]);
|
||||
model.save_changes();
|
||||
};
|
||||
addDataListener(api.view, watch.name, watch.scope, lodashDebounce(dataHandler, wait, debounceOpts))
|
||||
|
||||
} else if (watch.namespace === "signal") {
|
||||
const signalHandler = (_, value) => {
|
||||
model.set("_js_to_py_updates", [{
|
||||
namespace: "signal",
|
||||
name: watch.name,
|
||||
scope: watch.scope,
|
||||
value: cleanJson(value)
|
||||
}]);
|
||||
model.save_changes();
|
||||
};
|
||||
|
||||
addSignalListener(api.view, watch.name, watch.scope, lodashDebounce(signalHandler, wait, debounceOpts))
|
||||
}
|
||||
}
|
||||
|
||||
// Add signal/data updaters
|
||||
model.on('change:_py_to_js_updates', async (updates) => {
|
||||
for (const update of updates.changed._py_to_js_updates ?? []) {
|
||||
if (update.namespace === "signal") {
|
||||
setSignalValue(api.view, update.name, update.scope, update.value);
|
||||
} else if (update.namespace === "data") {
|
||||
setDataValue(api.view, update.name, update.scope, update.value);
|
||||
}
|
||||
}
|
||||
await api.view.runAsync();
|
||||
});
|
||||
}
|
||||
|
||||
model.on('change:spec', reembed);
|
||||
model.on('change:embed_options', reembed);
|
||||
model.on('change:debounce_wait', reembed);
|
||||
model.on('change:max_wait', reembed);
|
||||
await reembed();
|
||||
}
|
||||
|
||||
function cleanJson(data) {
|
||||
return JSON.parse(JSON.stringify(data))
|
||||
}
|
||||
|
||||
function getNestedRuntime(view, scope) {
|
||||
var runtime = view._runtime;
|
||||
for (const index of scope) {
|
||||
runtime = runtime.subcontext[index];
|
||||
}
|
||||
return runtime
|
||||
}
|
||||
|
||||
function lookupSignalOp(view, name, scope) {
|
||||
let parent_runtime = getNestedRuntime(view, scope);
|
||||
return parent_runtime.signals[name] ?? null;
|
||||
}
|
||||
|
||||
function dataRef(view, name, scope) {
|
||||
let parent_runtime = getNestedRuntime(view, scope);
|
||||
return parent_runtime.data[name];
|
||||
}
|
||||
|
||||
export function setSignalValue(view, name, scope, value) {
|
||||
let signal_op = lookupSignalOp(view, name, scope);
|
||||
view.update(signal_op, value);
|
||||
}
|
||||
|
||||
export function setDataValue(view, name, scope, value) {
|
||||
let dataset = dataRef(view, name, scope);
|
||||
let changeset = view.changeset().remove(() => true).insert(value)
|
||||
dataset.modified = true;
|
||||
view.pulse(dataset.input, changeset);
|
||||
}
|
||||
|
||||
export function addSignalListener(view, name, scope, handler) {
|
||||
let signal_op = lookupSignalOp(view, name, scope);
|
||||
return addOperatorListener(
|
||||
view,
|
||||
name,
|
||||
signal_op,
|
||||
handler,
|
||||
);
|
||||
}
|
||||
|
||||
export function addDataListener(view, name, scope, handler) {
|
||||
let dataset = dataRef(view, name, scope).values;
|
||||
return addOperatorListener(
|
||||
view,
|
||||
name,
|
||||
dataset,
|
||||
handler,
|
||||
);
|
||||
}
|
||||
|
||||
// Private helpers from Vega for dealing with nested signals/data
|
||||
function findOperatorHandler(op, handler) {
|
||||
const h = (op._targets || [])
|
||||
.filter(op => op._update && op._update.handler === handler);
|
||||
return h.length ? h[0] : null;
|
||||
}
|
||||
|
||||
function addOperatorListener(view, name, op, handler) {
|
||||
let h = findOperatorHandler(op, handler);
|
||||
if (!h) {
|
||||
h = trap(view, () => handler(name, op.value));
|
||||
h.handler = handler;
|
||||
view.on(op, null, h);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
function trap(view, fn) {
|
||||
return !fn ? null : function() {
|
||||
try {
|
||||
fn.apply(this, arguments);
|
||||
} catch (error) {
|
||||
view.error(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default { render }
|
||||
@@ -0,0 +1,404 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
from typing import Any
|
||||
|
||||
import anywidget
|
||||
import traitlets
|
||||
|
||||
import altair as alt
|
||||
from altair import TopLevelSpec
|
||||
from altair.utils._vegafusion_data import (
|
||||
compile_to_vegafusion_chart_state,
|
||||
using_vegafusion,
|
||||
)
|
||||
from altair.utils.selection import IndexSelection, IntervalSelection, PointSelection
|
||||
|
||||
_here = pathlib.Path(__file__).parent
|
||||
|
||||
|
||||
class Params(traitlets.HasTraits):
|
||||
"""Traitlet class storing a JupyterChart's params."""
|
||||
|
||||
def __init__(self, trait_values):
|
||||
super().__init__()
|
||||
|
||||
for key, value in trait_values.items():
|
||||
if isinstance(value, (int, float)):
|
||||
traitlet_type = traitlets.Float()
|
||||
elif isinstance(value, str):
|
||||
traitlet_type = traitlets.Unicode()
|
||||
elif isinstance(value, list):
|
||||
traitlet_type = traitlets.List()
|
||||
elif isinstance(value, dict):
|
||||
traitlet_type = traitlets.Dict()
|
||||
else:
|
||||
traitlet_type = traitlets.Any()
|
||||
|
||||
# Add the new trait.
|
||||
self.add_traits(**{key: traitlet_type})
|
||||
|
||||
# Set the trait's value.
|
||||
setattr(self, key, value)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Params({self.trait_values()})"
|
||||
|
||||
|
||||
class Selections(traitlets.HasTraits):
|
||||
"""Traitlet class storing a JupyterChart's selections."""
|
||||
|
||||
def __init__(self, trait_values):
|
||||
super().__init__()
|
||||
|
||||
for key, value in trait_values.items():
|
||||
if isinstance(value, IndexSelection):
|
||||
traitlet_type = traitlets.Instance(IndexSelection)
|
||||
elif isinstance(value, PointSelection):
|
||||
traitlet_type = traitlets.Instance(PointSelection)
|
||||
elif isinstance(value, IntervalSelection):
|
||||
traitlet_type = traitlets.Instance(IntervalSelection)
|
||||
else:
|
||||
msg = f"Unexpected selection type: {type(value)}"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Add the new trait.
|
||||
self.add_traits(**{key: traitlet_type})
|
||||
|
||||
# Set the trait's value.
|
||||
setattr(self, key, value)
|
||||
|
||||
# Make read-only
|
||||
self.observe(self._make_read_only, names=key)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Selections({self.trait_values()})"
|
||||
|
||||
def _make_read_only(self, change):
|
||||
"""Work around to make traits read-only, but still allow us to change them internally."""
|
||||
if change["name"] in self.traits() and change["old"] != change["new"]:
|
||||
self._set_value(change["name"], change["old"])
|
||||
msg = (
|
||||
"Selections may not be set from Python.\n"
|
||||
f"Attempted to set select: {change['name']}"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
def _set_value(self, key, value):
|
||||
self.unobserve(self._make_read_only, names=key)
|
||||
setattr(self, key, value)
|
||||
self.observe(self._make_read_only, names=key)
|
||||
|
||||
|
||||
def load_js_src() -> str:
|
||||
return (_here / "js" / "index.js").read_text()
|
||||
|
||||
|
||||
class JupyterChart(anywidget.AnyWidget):
|
||||
_esm = load_js_src()
|
||||
_css = r"""
|
||||
.vega-embed {
|
||||
/* Make sure action menu isn't cut off */
|
||||
overflow: visible;
|
||||
}
|
||||
"""
|
||||
|
||||
# Public traitlets
|
||||
chart = traitlets.Instance(TopLevelSpec, allow_none=True)
|
||||
spec = traitlets.Dict(allow_none=True).tag(sync=True)
|
||||
debounce_wait = traitlets.Float(default_value=10).tag(sync=True)
|
||||
max_wait = traitlets.Bool(default_value=True).tag(sync=True)
|
||||
local_tz = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
|
||||
debug = traitlets.Bool(default_value=False)
|
||||
embed_options = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
|
||||
|
||||
# Internal selection traitlets
|
||||
_selection_types = traitlets.Dict()
|
||||
_vl_selections = traitlets.Dict().tag(sync=True)
|
||||
|
||||
# Internal param traitlets
|
||||
_params = traitlets.Dict().tag(sync=True)
|
||||
|
||||
# Internal comm traitlets for VegaFusion support
|
||||
_chart_state = traitlets.Any(allow_none=True)
|
||||
_js_watch_plan = traitlets.Any(allow_none=True).tag(sync=True)
|
||||
_js_to_py_updates = traitlets.Any(allow_none=True).tag(sync=True)
|
||||
_py_to_js_updates = traitlets.Any(allow_none=True).tag(sync=True)
|
||||
|
||||
# Track whether charts are configured for offline use
|
||||
_is_offline = False
|
||||
|
||||
@classmethod
|
||||
def enable_offline(cls, offline: bool = True):
|
||||
"""
|
||||
Configure JupyterChart's offline behavior.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
offline: bool
|
||||
If True, configure JupyterChart to operate in offline mode where JavaScript
|
||||
dependencies are loaded from vl-convert.
|
||||
If False, configure it to operate in online mode where JavaScript dependencies
|
||||
are loaded from CDN dynamically. This is the default behavior.
|
||||
"""
|
||||
from altair.utils._importers import import_vl_convert, vl_version_for_vl_convert
|
||||
|
||||
if offline:
|
||||
if cls._is_offline:
|
||||
# Already offline
|
||||
return
|
||||
|
||||
vlc = import_vl_convert()
|
||||
|
||||
src_lines = load_js_src().split("\n")
|
||||
|
||||
# Remove leading lines with only whitespace, comments, or imports
|
||||
while src_lines and (
|
||||
len(src_lines[0].strip()) == 0
|
||||
or src_lines[0].startswith("import")
|
||||
or src_lines[0].startswith("//")
|
||||
):
|
||||
src_lines.pop(0)
|
||||
|
||||
src = "\n".join(src_lines)
|
||||
|
||||
# vl-convert's javascript_bundle function creates a self-contained JavaScript bundle
|
||||
# for JavaScript snippets that import from a small set of dependencies that
|
||||
# vl-convert includes. To see the available imports and their imported names, run
|
||||
# import vl_convert as vlc
|
||||
# help(vlc.javascript_bundle)
|
||||
bundled_src = vlc.javascript_bundle(
|
||||
src, vl_version=vl_version_for_vl_convert()
|
||||
)
|
||||
cls._esm = bundled_src
|
||||
cls._is_offline = True
|
||||
else:
|
||||
cls._esm = load_js_src()
|
||||
cls._is_offline = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chart: TopLevelSpec,
|
||||
debounce_wait: int = 10,
|
||||
max_wait: bool = True,
|
||||
debug: bool = False,
|
||||
embed_options: dict | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
Jupyter Widget for displaying and updating Altair Charts, and retrieving selection and parameter values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chart: Chart
|
||||
Altair Chart instance
|
||||
debounce_wait: int
|
||||
Debouncing wait time in milliseconds. Updates will be sent from the client to the kernel
|
||||
after debounce_wait milliseconds of no chart interactions.
|
||||
max_wait: bool
|
||||
If True (default), updates will be sent from the client to the kernel every debounce_wait
|
||||
milliseconds even if there are ongoing chart interactions. If False, updates will not be
|
||||
sent until chart interactions have completed.
|
||||
debug: bool
|
||||
If True, debug messages will be printed
|
||||
embed_options: dict
|
||||
Options to pass to vega-embed.
|
||||
See https://github.com/vega/vega-embed?tab=readme-ov-file#options
|
||||
"""
|
||||
self.params = Params({})
|
||||
self.selections = Selections({})
|
||||
super().__init__(
|
||||
chart=chart,
|
||||
debounce_wait=debounce_wait,
|
||||
max_wait=max_wait,
|
||||
debug=debug,
|
||||
embed_options=embed_options,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@traitlets.observe("chart")
|
||||
def _on_change_chart(self, change): # noqa: C901
|
||||
"""Updates the JupyterChart's internal state when the wrapped Chart instance changes."""
|
||||
new_chart = change.new
|
||||
selection_watches = []
|
||||
selection_types = {}
|
||||
initial_params = {}
|
||||
initial_vl_selections = {}
|
||||
empty_selections = {}
|
||||
|
||||
if new_chart is None:
|
||||
with self.hold_sync():
|
||||
self.spec = None
|
||||
self._selection_types = selection_types
|
||||
self._vl_selections = initial_vl_selections
|
||||
self._params = initial_params
|
||||
return
|
||||
|
||||
params = getattr(new_chart, "params", [])
|
||||
|
||||
if params is not alt.Undefined:
|
||||
for param in new_chart.params:
|
||||
if isinstance(param.name, alt.ParameterName):
|
||||
clean_name = param.name.to_json().strip('"')
|
||||
else:
|
||||
clean_name = param.name
|
||||
|
||||
select = getattr(param, "select", alt.Undefined)
|
||||
|
||||
if select != alt.Undefined:
|
||||
if not isinstance(select, dict):
|
||||
select = select.to_dict()
|
||||
|
||||
select_type = select["type"]
|
||||
if select_type == "point":
|
||||
if not (
|
||||
select.get("fields", None) or select.get("encodings", None)
|
||||
):
|
||||
# Point selection with no associated fields or encodings specified.
|
||||
# This is an index-based selection
|
||||
selection_types[clean_name] = "index"
|
||||
empty_selections[clean_name] = IndexSelection(
|
||||
name=clean_name, value=[], store=[]
|
||||
)
|
||||
else:
|
||||
selection_types[clean_name] = "point"
|
||||
empty_selections[clean_name] = PointSelection(
|
||||
name=clean_name, value=[], store=[]
|
||||
)
|
||||
elif select_type == "interval":
|
||||
selection_types[clean_name] = "interval"
|
||||
empty_selections[clean_name] = IntervalSelection(
|
||||
name=clean_name, value={}, store=[]
|
||||
)
|
||||
else:
|
||||
msg = f"Unexpected selection type {select.type}"
|
||||
raise ValueError(msg)
|
||||
selection_watches.append(clean_name)
|
||||
initial_vl_selections[clean_name] = {"value": None, "store": []}
|
||||
else:
|
||||
clean_value = param.value if param.value != alt.Undefined else None
|
||||
initial_params[clean_name] = clean_value
|
||||
|
||||
# Handle the params generated by transforms
|
||||
for param_name in collect_transform_params(new_chart):
|
||||
initial_params[param_name] = None
|
||||
|
||||
# Setup params
|
||||
self.params = Params(initial_params)
|
||||
|
||||
def on_param_traitlet_changed(param_change):
|
||||
new_params = dict(self._params)
|
||||
new_params[param_change["name"]] = param_change["new"]
|
||||
self._params = new_params
|
||||
|
||||
self.params.observe(on_param_traitlet_changed)
|
||||
|
||||
# Setup selections
|
||||
self.selections = Selections(empty_selections)
|
||||
|
||||
# Update properties all together
|
||||
with self.hold_sync():
|
||||
if using_vegafusion():
|
||||
if self.local_tz is None:
|
||||
self.spec = None
|
||||
|
||||
def on_local_tz_change(change):
|
||||
self._init_with_vegafusion(change["new"])
|
||||
|
||||
self.observe(on_local_tz_change, ["local_tz"])
|
||||
else:
|
||||
self._init_with_vegafusion(self.local_tz)
|
||||
else:
|
||||
self.spec = new_chart.to_dict()
|
||||
self._selection_types = selection_types
|
||||
self._vl_selections = initial_vl_selections
|
||||
self._params = initial_params
|
||||
|
||||
def _init_with_vegafusion(self, local_tz: str):
|
||||
if self.chart is not None:
|
||||
vegalite_spec = self.chart.to_dict(context={"pre_transform": False})
|
||||
with self.hold_sync():
|
||||
self._chart_state = compile_to_vegafusion_chart_state(
|
||||
vegalite_spec, local_tz
|
||||
)
|
||||
self._js_watch_plan = self._chart_state.get_watch_plan()[
|
||||
"client_to_server"
|
||||
]
|
||||
self.spec = self._chart_state.get_transformed_spec()
|
||||
|
||||
# Callback to update chart state and send updates back to client
|
||||
def on_js_to_py_updates(change):
|
||||
if self.debug:
|
||||
updates_str = json.dumps(change["new"], indent=2)
|
||||
print(
|
||||
f"JavaScript to Python VegaFusion updates:\n {updates_str}"
|
||||
)
|
||||
updates = self._chart_state.update(change["new"])
|
||||
if self.debug:
|
||||
updates_str = json.dumps(updates, indent=2)
|
||||
print(
|
||||
f"Python to JavaScript VegaFusion updates:\n {updates_str}"
|
||||
)
|
||||
self._py_to_js_updates = updates
|
||||
|
||||
self.observe(on_js_to_py_updates, ["_js_to_py_updates"])
|
||||
|
||||
@traitlets.observe("_params")
|
||||
def _on_change_params(self, change):
|
||||
for param_name, value in change.new.items():
|
||||
setattr(self.params, param_name, value)
|
||||
|
||||
@traitlets.observe("_vl_selections")
|
||||
def _on_change_selections(self, change):
|
||||
"""Updates the JupyterChart's public selections traitlet in response to changes that the JavaScript logic makes to the internal _selections traitlet."""
|
||||
for selection_name, selection_dict in change.new.items():
|
||||
value = selection_dict["value"]
|
||||
store = selection_dict["store"]
|
||||
selection_type = self._selection_types[selection_name]
|
||||
if selection_type == "index":
|
||||
self.selections._set_value(
|
||||
selection_name,
|
||||
IndexSelection.from_vega(selection_name, signal=value, store=store),
|
||||
)
|
||||
elif selection_type == "point":
|
||||
self.selections._set_value(
|
||||
selection_name,
|
||||
PointSelection.from_vega(selection_name, signal=value, store=store),
|
||||
)
|
||||
elif selection_type == "interval":
|
||||
self.selections._set_value(
|
||||
selection_name,
|
||||
IntervalSelection.from_vega(
|
||||
selection_name, signal=value, store=store
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def collect_transform_params(chart: TopLevelSpec) -> set[str]:
|
||||
"""
|
||||
Collect the names of params that are defined by transforms.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chart: Chart from which to extract transform params
|
||||
|
||||
Returns
|
||||
-------
|
||||
set of param names
|
||||
"""
|
||||
transform_params = set()
|
||||
|
||||
# Handle recursive case
|
||||
for prop in ("layer", "concat", "hconcat", "vconcat"):
|
||||
for child in getattr(chart, prop, []):
|
||||
transform_params.update(collect_transform_params(child))
|
||||
|
||||
# Handle chart's own transforms
|
||||
transforms = getattr(chart, "transform", [])
|
||||
transforms = transforms if transforms != alt.Undefined else []
|
||||
for tx in transforms:
|
||||
if hasattr(tx, "param"):
|
||||
transform_params.add(tx.param)
|
||||
|
||||
return transform_params
|
||||
0
myenv/lib/python3.11/site-packages/altair/py.typed
Normal file
0
myenv/lib/python3.11/site-packages/altair/py.typed
Normal file
321
myenv/lib/python3.11/site-packages/altair/theme.py
Normal file
321
myenv/lib/python3.11/site-packages/altair/theme.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""Customizing chart configuration defaults."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import wraps as _wraps
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import overload as _overload
|
||||
|
||||
from altair.vegalite.v5.schema._config import (
|
||||
AreaConfigKwds,
|
||||
AutoSizeParamsKwds,
|
||||
AxisConfigKwds,
|
||||
AxisResolveMapKwds,
|
||||
BarConfigKwds,
|
||||
BindCheckboxKwds,
|
||||
BindDirectKwds,
|
||||
BindInputKwds,
|
||||
BindRadioSelectKwds,
|
||||
BindRangeKwds,
|
||||
BoxPlotConfigKwds,
|
||||
BrushConfigKwds,
|
||||
CompositionConfigKwds,
|
||||
ConfigKwds,
|
||||
DateTimeKwds,
|
||||
DerivedStreamKwds,
|
||||
ErrorBandConfigKwds,
|
||||
ErrorBarConfigKwds,
|
||||
FeatureGeometryGeoJsonPropertiesKwds,
|
||||
FormatConfigKwds,
|
||||
GeoJsonFeatureCollectionKwds,
|
||||
GeoJsonFeatureKwds,
|
||||
GeometryCollectionKwds,
|
||||
GradientStopKwds,
|
||||
HeaderConfigKwds,
|
||||
IntervalSelectionConfigKwds,
|
||||
IntervalSelectionConfigWithoutTypeKwds,
|
||||
LegendConfigKwds,
|
||||
LegendResolveMapKwds,
|
||||
LegendStreamBindingKwds,
|
||||
LinearGradientKwds,
|
||||
LineConfigKwds,
|
||||
LineStringKwds,
|
||||
LocaleKwds,
|
||||
MarkConfigKwds,
|
||||
MergedStreamKwds,
|
||||
MultiLineStringKwds,
|
||||
MultiPointKwds,
|
||||
MultiPolygonKwds,
|
||||
NumberLocaleKwds,
|
||||
OverlayMarkDefKwds,
|
||||
PaddingKwds,
|
||||
PointKwds,
|
||||
PointSelectionConfigKwds,
|
||||
PointSelectionConfigWithoutTypeKwds,
|
||||
PolygonKwds,
|
||||
ProjectionConfigKwds,
|
||||
ProjectionKwds,
|
||||
RadialGradientKwds,
|
||||
RangeConfigKwds,
|
||||
RectConfigKwds,
|
||||
ResolveKwds,
|
||||
RowColKwds,
|
||||
ScaleConfigKwds,
|
||||
ScaleInvalidDataConfigKwds,
|
||||
ScaleResolveMapKwds,
|
||||
SelectionConfigKwds,
|
||||
StepKwds,
|
||||
StyleConfigIndexKwds,
|
||||
ThemeConfig,
|
||||
TickConfigKwds,
|
||||
TimeIntervalStepKwds,
|
||||
TimeLocaleKwds,
|
||||
TitleConfigKwds,
|
||||
TitleParamsKwds,
|
||||
TooltipContentKwds,
|
||||
TopLevelSelectionParameterKwds,
|
||||
VariableParameterKwds,
|
||||
ViewBackgroundKwds,
|
||||
ViewConfigKwds,
|
||||
)
|
||||
from altair.vegalite.v5.theme import themes as _themes
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
from typing import Any, Callable, Literal
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import LiteralString
|
||||
else:
|
||||
from typing_extensions import LiteralString
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import ParamSpec
|
||||
else:
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
from altair.utils.plugin_registry import Plugin
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
__all__ = [
|
||||
"AreaConfigKwds",
|
||||
"AutoSizeParamsKwds",
|
||||
"AxisConfigKwds",
|
||||
"AxisResolveMapKwds",
|
||||
"BarConfigKwds",
|
||||
"BindCheckboxKwds",
|
||||
"BindDirectKwds",
|
||||
"BindInputKwds",
|
||||
"BindRadioSelectKwds",
|
||||
"BindRangeKwds",
|
||||
"BoxPlotConfigKwds",
|
||||
"BrushConfigKwds",
|
||||
"CompositionConfigKwds",
|
||||
"ConfigKwds",
|
||||
"DateTimeKwds",
|
||||
"DerivedStreamKwds",
|
||||
"ErrorBandConfigKwds",
|
||||
"ErrorBarConfigKwds",
|
||||
"FeatureGeometryGeoJsonPropertiesKwds",
|
||||
"FormatConfigKwds",
|
||||
"GeoJsonFeatureCollectionKwds",
|
||||
"GeoJsonFeatureKwds",
|
||||
"GeometryCollectionKwds",
|
||||
"GradientStopKwds",
|
||||
"HeaderConfigKwds",
|
||||
"IntervalSelectionConfigKwds",
|
||||
"IntervalSelectionConfigWithoutTypeKwds",
|
||||
"LegendConfigKwds",
|
||||
"LegendResolveMapKwds",
|
||||
"LegendStreamBindingKwds",
|
||||
"LineConfigKwds",
|
||||
"LineStringKwds",
|
||||
"LinearGradientKwds",
|
||||
"LocaleKwds",
|
||||
"MarkConfigKwds",
|
||||
"MergedStreamKwds",
|
||||
"MultiLineStringKwds",
|
||||
"MultiPointKwds",
|
||||
"MultiPolygonKwds",
|
||||
"NumberLocaleKwds",
|
||||
"OverlayMarkDefKwds",
|
||||
"PaddingKwds",
|
||||
"PointKwds",
|
||||
"PointSelectionConfigKwds",
|
||||
"PointSelectionConfigWithoutTypeKwds",
|
||||
"PolygonKwds",
|
||||
"ProjectionConfigKwds",
|
||||
"ProjectionKwds",
|
||||
"RadialGradientKwds",
|
||||
"RangeConfigKwds",
|
||||
"RectConfigKwds",
|
||||
"ResolveKwds",
|
||||
"RowColKwds",
|
||||
"ScaleConfigKwds",
|
||||
"ScaleInvalidDataConfigKwds",
|
||||
"ScaleResolveMapKwds",
|
||||
"SelectionConfigKwds",
|
||||
"StepKwds",
|
||||
"StyleConfigIndexKwds",
|
||||
"ThemeConfig",
|
||||
"TickConfigKwds",
|
||||
"TimeIntervalStepKwds",
|
||||
"TimeLocaleKwds",
|
||||
"TitleConfigKwds",
|
||||
"TitleParamsKwds",
|
||||
"TooltipContentKwds",
|
||||
"TopLevelSelectionParameterKwds",
|
||||
"VariableParameterKwds",
|
||||
"ViewBackgroundKwds",
|
||||
"ViewConfigKwds",
|
||||
"active",
|
||||
"enable",
|
||||
"get",
|
||||
"names",
|
||||
"options",
|
||||
"register",
|
||||
"unregister",
|
||||
]
|
||||
|
||||
|
||||
def register(
|
||||
name: LiteralString, *, enable: bool
|
||||
) -> Callable[[Plugin[ThemeConfig]], Plugin[ThemeConfig]]:
|
||||
"""
|
||||
Decorator for registering a theme function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name
|
||||
Unique name assigned in registry.
|
||||
enable
|
||||
Auto-enable the wrapped theme.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Register and enable a theme::
|
||||
|
||||
import altair as alt
|
||||
from altair import theme
|
||||
|
||||
|
||||
@theme.register("param_font_size", enable=True)
|
||||
def custom_theme() -> theme.ThemeConfig:
|
||||
sizes = 12, 14, 16, 18, 20
|
||||
return {
|
||||
"autosize": {"contains": "content", "resize": True},
|
||||
"background": "#F3F2F1",
|
||||
"config": {
|
||||
"axisX": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]},
|
||||
"axisY": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]},
|
||||
"font": "'Lato', 'Segoe UI', Tahoma, Verdana, sans-serif",
|
||||
"headerColumn": {"labelFontSize": sizes[1]},
|
||||
"headerFacet": {"labelFontSize": sizes[1]},
|
||||
"headerRow": {"labelFontSize": sizes[1]},
|
||||
"legend": {"labelFontSize": sizes[0], "titleFontSize": sizes[1]},
|
||||
"text": {"fontSize": sizes[0]},
|
||||
"title": {"fontSize": sizes[-1]},
|
||||
},
|
||||
"height": {"step": 28},
|
||||
"width": 350,
|
||||
}
|
||||
|
||||
We can then see the ``name`` parameter displayed when checking::
|
||||
|
||||
theme.active
|
||||
"param_font_size"
|
||||
|
||||
Until another theme has been enabled, all charts will use defaults set in ``custom_theme()``::
|
||||
|
||||
from vega_datasets import data
|
||||
|
||||
source = data.stocks()
|
||||
lines = (
|
||||
alt.Chart(source, title=alt.Title("Stocks"))
|
||||
.mark_line()
|
||||
.encode(x="date:T", y="price:Q", color="symbol:N")
|
||||
)
|
||||
lines.interactive(bind_y=False)
|
||||
|
||||
"""
|
||||
|
||||
# HACK: See for `LiteralString` requirement in `name`
|
||||
# https://github.com/vega/altair/pull/3526#discussion_r1743350127
|
||||
def decorate(func: Plugin[ThemeConfig], /) -> Plugin[ThemeConfig]:
|
||||
_register(name, func)
|
||||
if enable:
|
||||
_themes.enable(name)
|
||||
|
||||
@_wraps(func)
|
||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> ThemeConfig:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorate
|
||||
|
||||
|
||||
def unregister(name: LiteralString) -> Plugin[ThemeConfig]:
|
||||
"""
|
||||
Remove and return a previously registered theme.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name
|
||||
Unique name assigned during ``alt.theme.register``.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
When ``name`` has not been registered.
|
||||
"""
|
||||
plugin = _register(name, None)
|
||||
if plugin is None:
|
||||
msg = (
|
||||
f"Found no theme named {name!r} in registry.\n"
|
||||
f"Registered themes:\n"
|
||||
f"{names()!r}"
|
||||
)
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
return plugin
|
||||
|
||||
|
||||
enable = _themes.enable
|
||||
get = _themes.get
|
||||
names = _themes.names
|
||||
active: str
|
||||
"""Return the name of the currently active theme."""
|
||||
options: dict[str, Any]
|
||||
"""Return the current themes options dictionary."""
|
||||
|
||||
|
||||
def __dir__() -> list[str]:
|
||||
return __all__
|
||||
|
||||
|
||||
@_overload
|
||||
def __getattr__(name: Literal["active"]) -> str: ... # type: ignore[misc]
|
||||
@_overload
|
||||
def __getattr__(name: Literal["options"]) -> dict[str, Any]: ... # type: ignore[misc]
|
||||
def __getattr__(name: str) -> Any:
|
||||
if name == "active":
|
||||
return _themes.active
|
||||
elif name == "options":
|
||||
return _themes.options
|
||||
else:
|
||||
msg = f"module {__name__!r} has no attribute {name!r}"
|
||||
raise AttributeError(msg)
|
||||
|
||||
|
||||
def _register(
|
||||
name: LiteralString, fn: Plugin[ThemeConfig] | None, /
|
||||
) -> Plugin[ThemeConfig] | None:
|
||||
if fn is None:
|
||||
return _themes._plugins.pop(name, None)
|
||||
elif _themes.plugin_type(fn):
|
||||
_themes._plugins[name] = fn
|
||||
return fn
|
||||
else:
|
||||
msg = f"{type(fn).__name__!r} is not a callable theme\n\n{fn!r}"
|
||||
raise TypeError(msg)
|
||||
96
myenv/lib/python3.11/site-packages/altair/typing/__init__.py
Normal file
96
myenv/lib/python3.11/site-packages/altair/typing/__init__.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""Public types to ease integrating with `altair`."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"ChannelAngle",
|
||||
"ChannelColor",
|
||||
"ChannelColumn",
|
||||
"ChannelDescription",
|
||||
"ChannelDetail",
|
||||
"ChannelFacet",
|
||||
"ChannelFill",
|
||||
"ChannelFillOpacity",
|
||||
"ChannelHref",
|
||||
"ChannelKey",
|
||||
"ChannelLatitude",
|
||||
"ChannelLatitude2",
|
||||
"ChannelLongitude",
|
||||
"ChannelLongitude2",
|
||||
"ChannelOpacity",
|
||||
"ChannelOrder",
|
||||
"ChannelRadius",
|
||||
"ChannelRadius2",
|
||||
"ChannelRow",
|
||||
"ChannelShape",
|
||||
"ChannelSize",
|
||||
"ChannelStroke",
|
||||
"ChannelStrokeDash",
|
||||
"ChannelStrokeOpacity",
|
||||
"ChannelStrokeWidth",
|
||||
"ChannelText",
|
||||
"ChannelTheta",
|
||||
"ChannelTheta2",
|
||||
"ChannelTooltip",
|
||||
"ChannelUrl",
|
||||
"ChannelX",
|
||||
"ChannelX2",
|
||||
"ChannelXError",
|
||||
"ChannelXError2",
|
||||
"ChannelXOffset",
|
||||
"ChannelY",
|
||||
"ChannelY2",
|
||||
"ChannelYError",
|
||||
"ChannelYError2",
|
||||
"ChannelYOffset",
|
||||
"ChartType",
|
||||
"EncodeKwds",
|
||||
"Optional",
|
||||
"is_chart_type",
|
||||
]
|
||||
|
||||
from altair.utils.schemapi import Optional
|
||||
from altair.vegalite.v5.api import ChartType, is_chart_type
|
||||
from altair.vegalite.v5.schema.channels import (
|
||||
ChannelAngle,
|
||||
ChannelColor,
|
||||
ChannelColumn,
|
||||
ChannelDescription,
|
||||
ChannelDetail,
|
||||
ChannelFacet,
|
||||
ChannelFill,
|
||||
ChannelFillOpacity,
|
||||
ChannelHref,
|
||||
ChannelKey,
|
||||
ChannelLatitude,
|
||||
ChannelLatitude2,
|
||||
ChannelLongitude,
|
||||
ChannelLongitude2,
|
||||
ChannelOpacity,
|
||||
ChannelOrder,
|
||||
ChannelRadius,
|
||||
ChannelRadius2,
|
||||
ChannelRow,
|
||||
ChannelShape,
|
||||
ChannelSize,
|
||||
ChannelStroke,
|
||||
ChannelStrokeDash,
|
||||
ChannelStrokeOpacity,
|
||||
ChannelStrokeWidth,
|
||||
ChannelText,
|
||||
ChannelTheta,
|
||||
ChannelTheta2,
|
||||
ChannelTooltip,
|
||||
ChannelUrl,
|
||||
ChannelX,
|
||||
ChannelX2,
|
||||
ChannelXError,
|
||||
ChannelXError2,
|
||||
ChannelXOffset,
|
||||
ChannelY,
|
||||
ChannelY2,
|
||||
ChannelYError,
|
||||
ChannelYError2,
|
||||
ChannelYOffset,
|
||||
EncodeKwds,
|
||||
)
|
||||
37
myenv/lib/python3.11/site-packages/altair/utils/__init__.py
Normal file
37
myenv/lib/python3.11/site-packages/altair/utils/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from .core import (
|
||||
SHORTHAND_KEYS,
|
||||
display_traceback,
|
||||
infer_encoding_types,
|
||||
infer_vegalite_type_for_pandas,
|
||||
parse_shorthand,
|
||||
sanitize_narwhals_dataframe,
|
||||
sanitize_pandas_dataframe,
|
||||
update_nested,
|
||||
use_signature,
|
||||
)
|
||||
from .deprecation import AltairDeprecationWarning, deprecated, deprecated_warn
|
||||
from .html import spec_to_html
|
||||
from .plugin_registry import PluginRegistry
|
||||
from .schemapi import Optional, SchemaBase, SchemaLike, Undefined, is_undefined
|
||||
|
||||
__all__ = (
|
||||
"SHORTHAND_KEYS",
|
||||
"AltairDeprecationWarning",
|
||||
"Optional",
|
||||
"PluginRegistry",
|
||||
"SchemaBase",
|
||||
"SchemaLike",
|
||||
"Undefined",
|
||||
"deprecated",
|
||||
"deprecated_warn",
|
||||
"display_traceback",
|
||||
"infer_encoding_types",
|
||||
"infer_vegalite_type_for_pandas",
|
||||
"is_undefined",
|
||||
"parse_shorthand",
|
||||
"sanitize_narwhals_dataframe",
|
||||
"sanitize_pandas_dataframe",
|
||||
"spec_to_html",
|
||||
"update_nested",
|
||||
"use_signature",
|
||||
)
|
||||
164
myenv/lib/python3.11/site-packages/altair/utils/_dfi_types.py
Normal file
164
myenv/lib/python3.11/site-packages/altair/utils/_dfi_types.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# DataFrame Interchange Protocol Types
|
||||
# Copied from https://data-apis.org/dataframe-protocol/latest/API.html,
|
||||
# changed ABCs to Protocols, and subset the type hints to only those that are
|
||||
# relevant for Altair.
|
||||
#
|
||||
# These classes are only for use in type signatures
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from typing import TYPE_CHECKING, Any, Protocol
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
|
||||
|
||||
class DtypeKind(enum.IntEnum):
|
||||
"""
|
||||
Integer enum for data types.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
INT : int
|
||||
Matches to signed integer data type.
|
||||
UINT : int
|
||||
Matches to unsigned integer data type.
|
||||
FLOAT : int
|
||||
Matches to floating point data type.
|
||||
BOOL : int
|
||||
Matches to boolean data type.
|
||||
STRING : int
|
||||
Matches to string data type (UTF-8 encoded).
|
||||
DATETIME : int
|
||||
Matches to datetime data type.
|
||||
CATEGORICAL : int
|
||||
Matches to categorical data type.
|
||||
"""
|
||||
|
||||
INT = 0
|
||||
UINT = 1
|
||||
FLOAT = 2
|
||||
BOOL = 20
|
||||
STRING = 21 # UTF-8
|
||||
DATETIME = 22
|
||||
CATEGORICAL = 23
|
||||
|
||||
|
||||
# Type hint of first element would actually be DtypeKind but can't use that
|
||||
# as other libraries won't use an instance of our own Enum in this module but have
|
||||
# their own. Type checkers will raise an error on that even though the enums
|
||||
# are identical.
|
||||
class Column(Protocol):
|
||||
@property
|
||||
def dtype(self) -> tuple[Any, int, str, str]:
|
||||
"""
|
||||
Dtype description as a tuple ``(kind, bit-width, format string, endianness)``.
|
||||
|
||||
Bit-width : the number of bits as an integer
|
||||
Format string : data type description format string in Apache Arrow C
|
||||
Data Interface format.
|
||||
Endianness : current only native endianness (``=``) is supported
|
||||
|
||||
Notes
|
||||
-----
|
||||
- Kind specifiers are aligned with DLPack where possible (hence the
|
||||
jump to 20, leave enough room for future extension)
|
||||
- Masks must be specified as boolean with either bit width 1 (for bit
|
||||
masks) or 8 (for byte masks).
|
||||
- Dtype width in bits was preferred over bytes
|
||||
- Endianness isn't too useful, but included now in case in the future
|
||||
we need to support non-native endianness
|
||||
- Went with Apache Arrow format strings over NumPy format strings
|
||||
because they're more complete from a dataframe perspective
|
||||
- Format strings are mostly useful for datetime specification, and
|
||||
for categoricals.
|
||||
- For categoricals, the format string describes the type of the
|
||||
categorical in the data buffer. In case of a separate encoding of
|
||||
the categorical (e.g. an integer to string mapping), this can
|
||||
be derived from ``self.describe_categorical``.
|
||||
- Data types not included: complex, Arrow-style null, binary, decimal,
|
||||
and nested (list, struct, map, union) dtypes.
|
||||
"""
|
||||
...
|
||||
|
||||
# Have to use a generic Any return type as not all libraries who implement
|
||||
# the dataframe interchange protocol implement the TypedDict that is usually
|
||||
# returned here in the same way. As TypedDicts are invariant, even a slight change
|
||||
# will lead to an error by a type checker. See PR in which this code was added
|
||||
# for details.
|
||||
@property
|
||||
def describe_categorical(self) -> Any:
|
||||
"""
|
||||
If the dtype is categorical, there are two options.
|
||||
|
||||
- There are only values in the data buffer.
|
||||
- There is a separate non-categorical Column encoding categorical values.
|
||||
|
||||
Raises TypeError if the dtype is not categorical
|
||||
|
||||
Returns the dictionary with description on how to interpret the data buffer:
|
||||
- "is_ordered" : bool, whether the ordering of dictionary indices is
|
||||
semantically meaningful.
|
||||
- "is_dictionary" : bool, whether a mapping of
|
||||
categorical values to other objects exists
|
||||
- "categories" : Column representing the (implicit) mapping of indices to
|
||||
category values (e.g. an array of cat1, cat2, ...).
|
||||
None if not a dictionary-style categorical.
|
||||
|
||||
TBD: are there any other in-memory representations that are needed?
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class DataFrame(Protocol):
|
||||
"""
|
||||
A data frame class, with only the methods required by the interchange protocol defined.
|
||||
|
||||
A "data frame" represents an ordered collection of named columns.
|
||||
A column's "name" must be a unique string.
|
||||
Columns may be accessed by name or by position.
|
||||
|
||||
This could be a public data frame class, or an object with the methods and
|
||||
attributes defined on this DataFrame class could be returned from the
|
||||
``__dataframe__`` method of a public data frame class in a library adhering
|
||||
to the dataframe interchange protocol specification.
|
||||
"""
|
||||
|
||||
def __dataframe__(
|
||||
self, nan_as_null: bool = False, allow_copy: bool = True
|
||||
) -> DataFrame:
|
||||
"""
|
||||
Construct a new exchange object, potentially changing the parameters.
|
||||
|
||||
``nan_as_null`` is a keyword intended for the consumer to tell the
|
||||
producer to overwrite null values in the data with ``NaN``.
|
||||
It is intended for cases where the consumer does not support the bit
|
||||
mask or byte mask that is the producer's native representation.
|
||||
``allow_copy`` is a keyword that defines whether or not the library is
|
||||
allowed to make a copy of the data. For example, copying data would be
|
||||
necessary if a library supports strided buffers, given that this protocol
|
||||
specifies contiguous buffers.
|
||||
"""
|
||||
...
|
||||
|
||||
def column_names(self) -> Iterable[str]:
|
||||
"""Return an iterator yielding the column names."""
|
||||
...
|
||||
|
||||
def get_column_by_name(self, name: str) -> Column:
|
||||
"""Return the column whose name is the indicated name."""
|
||||
...
|
||||
|
||||
def get_chunks(self, n_chunks: int | None = None) -> Iterable[DataFrame]:
|
||||
"""
|
||||
Return an iterator yielding the chunks.
|
||||
|
||||
By default (None), yields the chunks that the data is stored as by the
|
||||
producer. If given, ``n_chunks`` must be a multiple of
|
||||
``self.num_chunks()``, meaning the producer must subdivide each chunk
|
||||
before yielding it.
|
||||
|
||||
Note that the producer must ensure that all columns are chunked the
|
||||
same way.
|
||||
"""
|
||||
...
|
||||
113
myenv/lib/python3.11/site-packages/altair/utils/_importers.py
Normal file
113
myenv/lib/python3.11/site-packages/altair/utils/_importers.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from importlib.metadata import version as importlib_version
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from types import ModuleType
|
||||
|
||||
|
||||
def import_vegafusion() -> ModuleType:
|
||||
min_version = "1.5.0"
|
||||
try:
|
||||
import vegafusion as vf
|
||||
|
||||
version = importlib_version("vegafusion")
|
||||
if Version(version) >= Version("2.0.0a0"):
|
||||
# In VegaFusion 2.0 there is no vegafusion-python-embed package
|
||||
return vf
|
||||
else:
|
||||
embed_version = importlib_version("vegafusion-python-embed")
|
||||
if version != embed_version or Version(version) < Version(min_version):
|
||||
msg = (
|
||||
"The versions of the vegafusion and vegafusion-python-embed packages must match\n"
|
||||
f"and must be version {min_version} or greater.\n"
|
||||
f"Found:\n"
|
||||
f" - vegafusion=={version}\n"
|
||||
f" - vegafusion-python-embed=={embed_version}\n"
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
return vf
|
||||
except ImportError as err:
|
||||
msg = (
|
||||
'The "vegafusion" data transformer and chart.transformed_data feature requires\n'
|
||||
f"version {min_version} or greater of the 'vegafusion-python-embed' and 'vegafusion' packages.\n"
|
||||
"These can be installed with pip using:\n"
|
||||
f' pip install "vegafusion[embed]>={min_version}"\n'
|
||||
"Or with conda using:\n"
|
||||
f' conda install -c conda-forge "vegafusion-python-embed>={min_version}" '
|
||||
f'"vegafusion>={min_version}"\n\n'
|
||||
f"ImportError: {err.args[0]}"
|
||||
)
|
||||
raise ImportError(msg) from err
|
||||
|
||||
|
||||
def import_vl_convert() -> ModuleType:
|
||||
min_version = "1.6.0"
|
||||
try:
|
||||
version = importlib_version("vl-convert-python")
|
||||
if Version(version) < Version(min_version):
|
||||
msg = (
|
||||
f"The vl-convert-python package must be version {min_version} or greater. "
|
||||
f"Found version {version}"
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
import vl_convert as vlc
|
||||
|
||||
return vlc
|
||||
except ImportError as err:
|
||||
msg = (
|
||||
f"The vl-convert Vega-Lite compiler and file export feature requires\n"
|
||||
f"version {min_version} or greater of the 'vl-convert-python' package. \n"
|
||||
f"This can be installed with pip using:\n"
|
||||
f' pip install "vl-convert-python>={min_version}"\n'
|
||||
"or conda:\n"
|
||||
f' conda install -c conda-forge "vl-convert-python>={min_version}"\n\n'
|
||||
f"ImportError: {err.args[0]}"
|
||||
)
|
||||
raise ImportError(msg) from err
|
||||
|
||||
|
||||
def vl_version_for_vl_convert() -> str:
|
||||
from altair.vegalite import SCHEMA_VERSION
|
||||
|
||||
# Compute VlConvert's vl_version string (of the form 'v5_2')
|
||||
# from SCHEMA_VERSION (of the form 'v5.2.0')
|
||||
return "_".join(SCHEMA_VERSION.split(".")[:2])
|
||||
|
||||
|
||||
def import_pyarrow_interchange() -> ModuleType:
|
||||
min_version = "11.0.0"
|
||||
try:
|
||||
version = importlib_version("pyarrow")
|
||||
|
||||
if Version(version) < Version(min_version):
|
||||
msg = (
|
||||
f"The pyarrow package must be version {min_version} or greater. "
|
||||
f"Found version {version}"
|
||||
)
|
||||
raise RuntimeError(msg)
|
||||
import pyarrow.interchange as pi
|
||||
|
||||
return pi
|
||||
except ImportError as err:
|
||||
msg = (
|
||||
f"Usage of the DataFrame Interchange Protocol requires\n"
|
||||
f"version {min_version} or greater of the pyarrow package. \n"
|
||||
f"This can be installed with pip using:\n"
|
||||
f' pip install "pyarrow>={min_version}"\n'
|
||||
"or conda:\n"
|
||||
f' conda install -c conda-forge "pyarrow>={min_version}"\n\n'
|
||||
f"ImportError: {err.args[0]}"
|
||||
)
|
||||
raise ImportError(msg) from err
|
||||
|
||||
|
||||
def pyarrow_available() -> bool:
|
||||
try:
|
||||
import_pyarrow_interchange()
|
||||
return True
|
||||
except (ImportError, RuntimeError):
|
||||
return False
|
||||
75
myenv/lib/python3.11/site-packages/altair/utils/_show.py
Normal file
75
myenv/lib/python3.11/site-packages/altair/utils/_show.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import webbrowser
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
|
||||
|
||||
def open_html_in_browser(
|
||||
html: str | bytes,
|
||||
using: str | Iterable[str] | None = None,
|
||||
port: int | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Display an html document in a web browser without creating a temp file.
|
||||
|
||||
Instantiates a simple http server and uses the webbrowser module to
|
||||
open the server's URL
|
||||
|
||||
Parameters
|
||||
----------
|
||||
html: str
|
||||
HTML string to display
|
||||
using: str or iterable of str
|
||||
Name of the web browser to open (e.g. "chrome", "firefox", etc.).
|
||||
If an iterable, choose the first browser available on the system.
|
||||
If none, choose the system default browser.
|
||||
port: int
|
||||
Port to use. Defaults to a random port
|
||||
"""
|
||||
# Encode html to bytes
|
||||
html_bytes = html.encode("utf8") if isinstance(html, str) else html
|
||||
|
||||
browser = None
|
||||
|
||||
if using is None:
|
||||
browser = webbrowser.get(None)
|
||||
else:
|
||||
# normalize using to an iterable
|
||||
if isinstance(using, str):
|
||||
using = [using]
|
||||
|
||||
for browser_key in using:
|
||||
try:
|
||||
browser = webbrowser.get(browser_key)
|
||||
if browser is not None:
|
||||
break
|
||||
except webbrowser.Error:
|
||||
pass
|
||||
|
||||
if browser is None:
|
||||
raise ValueError("Failed to locate a browser with name in " + str(using))
|
||||
|
||||
class OneShotRequestHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self) -> None:
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
|
||||
bufferSize = 1024 * 1024
|
||||
for i in range(0, len(html_bytes), bufferSize):
|
||||
self.wfile.write(html_bytes[i : i + bufferSize])
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# Silence stderr logging
|
||||
pass
|
||||
|
||||
# Use specified port if provided, otherwise choose a random port (port value of 0)
|
||||
server = HTTPServer(
|
||||
("127.0.0.1", port if port is not None else 0), OneShotRequestHandler
|
||||
)
|
||||
browser.open(f"http://127.0.0.1:{server.server_port}")
|
||||
server.handle_request()
|
||||
@@ -0,0 +1,567 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, overload
|
||||
|
||||
from altair import (
|
||||
Chart,
|
||||
ConcatChart,
|
||||
ConcatSpecGenericSpec,
|
||||
FacetChart,
|
||||
FacetedUnitSpec,
|
||||
FacetSpec,
|
||||
HConcatChart,
|
||||
HConcatSpecGenericSpec,
|
||||
LayerChart,
|
||||
LayerSpec,
|
||||
NonNormalizedSpec,
|
||||
TopLevelConcatSpec,
|
||||
TopLevelFacetSpec,
|
||||
TopLevelHConcatSpec,
|
||||
TopLevelLayerSpec,
|
||||
TopLevelUnitSpec,
|
||||
TopLevelVConcatSpec,
|
||||
UnitSpec,
|
||||
UnitSpecWithFrame,
|
||||
VConcatChart,
|
||||
VConcatSpecGenericSpec,
|
||||
data_transformers,
|
||||
)
|
||||
from altair.utils._vegafusion_data import get_inline_tables, import_vegafusion
|
||||
from altair.utils.schemapi import Undefined
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
from collections.abc import Iterable
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from altair.typing import ChartType
|
||||
from altair.utils.core import DataFrameLike
|
||||
|
||||
Scope: TypeAlias = tuple[int, ...]
|
||||
FacetMapping: TypeAlias = dict[tuple[str, Scope], tuple[str, Scope]]
|
||||
|
||||
|
||||
# For the transformed_data functionality, the chart classes in the values
|
||||
# can be considered equivalent to the chart class in the key.
|
||||
_chart_class_mapping = {
|
||||
Chart: (
|
||||
Chart,
|
||||
TopLevelUnitSpec,
|
||||
FacetedUnitSpec,
|
||||
UnitSpec,
|
||||
UnitSpecWithFrame,
|
||||
NonNormalizedSpec,
|
||||
),
|
||||
LayerChart: (LayerChart, TopLevelLayerSpec, LayerSpec),
|
||||
ConcatChart: (ConcatChart, TopLevelConcatSpec, ConcatSpecGenericSpec),
|
||||
HConcatChart: (HConcatChart, TopLevelHConcatSpec, HConcatSpecGenericSpec),
|
||||
VConcatChart: (VConcatChart, TopLevelVConcatSpec, VConcatSpecGenericSpec),
|
||||
FacetChart: (FacetChart, TopLevelFacetSpec, FacetSpec),
|
||||
}
|
||||
|
||||
|
||||
@overload
|
||||
def transformed_data(
|
||||
chart: Chart | FacetChart,
|
||||
row_limit: int | None = None,
|
||||
exclude: Iterable[str] | None = None,
|
||||
) -> DataFrameLike | None: ...
|
||||
|
||||
|
||||
@overload
|
||||
def transformed_data(
|
||||
chart: LayerChart | HConcatChart | VConcatChart | ConcatChart,
|
||||
row_limit: int | None = None,
|
||||
exclude: Iterable[str] | None = None,
|
||||
) -> list[DataFrameLike]: ...
|
||||
|
||||
|
||||
def transformed_data(chart, row_limit=None, exclude=None):
|
||||
"""
|
||||
Evaluate a Chart's transforms.
|
||||
|
||||
Evaluate the data transforms associated with a Chart and return the
|
||||
transformed data as one or more DataFrames
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chart : Chart, FacetChart, LayerChart, HConcatChart, VConcatChart, or ConcatChart
|
||||
Altair chart to evaluate transforms on
|
||||
row_limit : int (optional)
|
||||
Maximum number of rows to return for each DataFrame. None (default) for unlimited
|
||||
exclude : iterable of str
|
||||
Set of the names of charts to exclude
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame or list of DataFrames or None
|
||||
If input chart is a Chart or Facet Chart, returns a DataFrame of the
|
||||
transformed data. Otherwise, returns a list of DataFrames of the
|
||||
transformed data
|
||||
"""
|
||||
vf = import_vegafusion()
|
||||
# Add mark if none is specified to satisfy Vega-Lite
|
||||
if isinstance(chart, Chart) and chart.mark == Undefined:
|
||||
chart = chart.mark_point()
|
||||
|
||||
# Deep copy chart so that we can rename marks without affecting caller
|
||||
chart = chart.copy(deep=True)
|
||||
|
||||
# Ensure that all views are named so that we can look them up in the
|
||||
# resulting Vega specification
|
||||
chart_names = name_views(chart, 0, exclude=exclude)
|
||||
|
||||
# Compile to Vega and extract inline DataFrames
|
||||
with data_transformers.enable("vegafusion"):
|
||||
vega_spec = chart.to_dict(format="vega", context={"pre_transform": False})
|
||||
inline_datasets = get_inline_tables(vega_spec)
|
||||
|
||||
# Build mapping from mark names to vega datasets
|
||||
facet_mapping = get_facet_mapping(vega_spec)
|
||||
dataset_mapping = get_datasets_for_view_names(vega_spec, chart_names, facet_mapping)
|
||||
|
||||
# Build a list of vega dataset names that corresponds to the order
|
||||
# of the chart components
|
||||
dataset_names = []
|
||||
for chart_name in chart_names:
|
||||
if chart_name in dataset_mapping:
|
||||
dataset_names.append(dataset_mapping[chart_name])
|
||||
else:
|
||||
msg = "Failed to locate all datasets"
|
||||
raise ValueError(msg)
|
||||
|
||||
# Extract transformed datasets with VegaFusion
|
||||
datasets, _ = vf.runtime.pre_transform_datasets(
|
||||
vega_spec,
|
||||
dataset_names,
|
||||
row_limit=row_limit,
|
||||
inline_datasets=inline_datasets,
|
||||
)
|
||||
|
||||
if isinstance(chart, (Chart, FacetChart)):
|
||||
# Return DataFrame (or None if it was excluded) if input was a simple Chart
|
||||
if not datasets:
|
||||
return None
|
||||
else:
|
||||
return datasets[0]
|
||||
else:
|
||||
# Otherwise return the list of DataFrames
|
||||
return datasets
|
||||
|
||||
|
||||
# The equivalent classes from _chart_class_mapping should also be added
|
||||
# to the type hints below for `chart` as the function would also work for them.
|
||||
# However, this was not possible so far as mypy then complains about
|
||||
# "Overloaded function signatures 1 and 2 overlap with incompatible return types [misc]"
|
||||
# This might be due to the complex type hierarchy of the chart classes.
|
||||
# See also https://github.com/python/mypy/issues/5119
|
||||
# and https://github.com/python/mypy/issues/4020 which show that mypy might not have
|
||||
# a very consistent behavior for overloaded functions.
|
||||
# The same error appeared when trying it with Protocols for the concat and layer charts.
|
||||
# This function is only used internally and so we accept this inconsistency for now.
|
||||
def name_views(
|
||||
chart: ChartType, i: int = 0, exclude: Iterable[str] | None = None
|
||||
) -> list[str]:
|
||||
"""
|
||||
Name unnamed chart views.
|
||||
|
||||
Name unnamed charts views so that we can look them up later in
|
||||
the compiled Vega spec.
|
||||
|
||||
Note: This function mutates the input chart by applying names to
|
||||
unnamed views.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chart : Chart, FacetChart, LayerChart, HConcatChart, VConcatChart, or ConcatChart
|
||||
Altair chart to apply names to
|
||||
i : int (default 0)
|
||||
Starting chart index
|
||||
exclude : iterable of str
|
||||
Names of charts to exclude
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of str
|
||||
List of the names of the charts and subcharts
|
||||
"""
|
||||
exclude = set(exclude) if exclude is not None else set()
|
||||
if isinstance(
|
||||
chart, (_chart_class_mapping[Chart], _chart_class_mapping[FacetChart])
|
||||
):
|
||||
if chart.name not in exclude:
|
||||
if chart.name in {None, Undefined}:
|
||||
# Add name since none is specified
|
||||
chart.name = Chart._get_name()
|
||||
return [chart.name]
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
subcharts: list[Any]
|
||||
if isinstance(chart, _chart_class_mapping[LayerChart]):
|
||||
subcharts = chart.layer
|
||||
elif isinstance(chart, _chart_class_mapping[HConcatChart]):
|
||||
subcharts = chart.hconcat
|
||||
elif isinstance(chart, _chart_class_mapping[VConcatChart]):
|
||||
subcharts = chart.vconcat
|
||||
elif isinstance(chart, _chart_class_mapping[ConcatChart]):
|
||||
subcharts = chart.concat
|
||||
else:
|
||||
msg = (
|
||||
"transformed_data accepts an instance of "
|
||||
"Chart, FacetChart, LayerChart, HConcatChart, VConcatChart, or ConcatChart\n"
|
||||
f"Received value of type: {type(chart)}"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
chart_names: list[str] = []
|
||||
for subchart in subcharts:
|
||||
for name in name_views(subchart, i=i + len(chart_names), exclude=exclude):
|
||||
chart_names.append(name)
|
||||
return chart_names
|
||||
|
||||
|
||||
def get_group_mark_for_scope(
|
||||
vega_spec: dict[str, Any], scope: Scope
|
||||
) -> dict[str, Any] | None:
|
||||
"""
|
||||
Get the group mark at a particular scope.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vega_spec : dict
|
||||
Top-level Vega specification dictionary
|
||||
scope : tuple of int
|
||||
Scope tuple. If empty, the original Vega specification is returned.
|
||||
Otherwise, the nested group mark at the scope specified is returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict or None
|
||||
Top-level Vega spec (if scope is empty)
|
||||
or group mark (if scope is non-empty)
|
||||
or None (if group mark at scope does not exist)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> spec = {
|
||||
... "marks": [
|
||||
... {"type": "group", "marks": [{"type": "symbol"}]},
|
||||
... {"type": "group", "marks": [{"type": "rect"}]},
|
||||
... ]
|
||||
... }
|
||||
>>> get_group_mark_for_scope(spec, (1,))
|
||||
{'type': 'group', 'marks': [{'type': 'rect'}]}
|
||||
"""
|
||||
group = vega_spec
|
||||
|
||||
# Find group at scope
|
||||
for scope_value in scope:
|
||||
group_index = 0
|
||||
child_group = None
|
||||
for mark in group.get("marks", []):
|
||||
if mark.get("type") == "group":
|
||||
if group_index == scope_value:
|
||||
child_group = mark
|
||||
break
|
||||
group_index += 1
|
||||
if child_group is None:
|
||||
return None
|
||||
group = child_group
|
||||
|
||||
return group
|
||||
|
||||
|
||||
def get_datasets_for_scope(vega_spec: dict[str, Any], scope: Scope) -> list[str]:
|
||||
"""
|
||||
Get the names of the datasets that are defined at a given scope.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vega_spec : dict
|
||||
Top-leve Vega specification
|
||||
scope : tuple of int
|
||||
Scope tuple. If empty, the names of top-level datasets are returned
|
||||
Otherwise, the names of the datasets defined in the nested group mark
|
||||
at the specified scope are returned.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of str
|
||||
List of the names of the datasets defined at the specified scope
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> spec = {
|
||||
... "data": [{"name": "data1"}],
|
||||
... "marks": [
|
||||
... {
|
||||
... "type": "group",
|
||||
... "data": [{"name": "data2"}],
|
||||
... "marks": [{"type": "symbol"}],
|
||||
... },
|
||||
... {
|
||||
... "type": "group",
|
||||
... "data": [
|
||||
... {"name": "data3"},
|
||||
... {"name": "data4"},
|
||||
... ],
|
||||
... "marks": [{"type": "rect"}],
|
||||
... },
|
||||
... ],
|
||||
... }
|
||||
|
||||
>>> get_datasets_for_scope(spec, ())
|
||||
['data1']
|
||||
|
||||
>>> get_datasets_for_scope(spec, (0,))
|
||||
['data2']
|
||||
|
||||
>>> get_datasets_for_scope(spec, (1,))
|
||||
['data3', 'data4']
|
||||
|
||||
Returns empty when no group mark exists at scope
|
||||
>>> get_datasets_for_scope(spec, (1, 3))
|
||||
[]
|
||||
"""
|
||||
group = get_group_mark_for_scope(vega_spec, scope) or {}
|
||||
|
||||
# get datasets from group
|
||||
datasets = []
|
||||
for dataset in group.get("data", []):
|
||||
datasets.append(dataset["name"])
|
||||
|
||||
# Add facet dataset
|
||||
facet_dataset = group.get("from", {}).get("facet", {}).get("name", None)
|
||||
if facet_dataset:
|
||||
datasets.append(facet_dataset)
|
||||
return datasets
|
||||
|
||||
|
||||
def get_definition_scope_for_data_reference(
|
||||
vega_spec: dict[str, Any], data_name: str, usage_scope: Scope
|
||||
) -> Scope | None:
|
||||
"""
|
||||
Return the scope that a dataset is defined at, for a given usage scope.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vega_spec: dict
|
||||
Top-level Vega specification
|
||||
data_name: str
|
||||
The name of a dataset reference
|
||||
usage_scope: tuple of int
|
||||
The scope that the dataset is referenced in
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple of int
|
||||
The scope where the referenced dataset is defined,
|
||||
or None if no such dataset is found
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> spec = {
|
||||
... "data": [{"name": "data1"}],
|
||||
... "marks": [
|
||||
... {
|
||||
... "type": "group",
|
||||
... "data": [{"name": "data2"}],
|
||||
... "marks": [
|
||||
... {
|
||||
... "type": "symbol",
|
||||
... "encode": {
|
||||
... "update": {
|
||||
... "x": {"field": "x", "data": "data1"},
|
||||
... "y": {"field": "y", "data": "data2"},
|
||||
... }
|
||||
... },
|
||||
... }
|
||||
... ],
|
||||
... }
|
||||
... ],
|
||||
... }
|
||||
|
||||
data1 is referenced at scope [0] and defined at scope []
|
||||
>>> get_definition_scope_for_data_reference(spec, "data1", (0,))
|
||||
()
|
||||
|
||||
data2 is referenced at scope [0] and defined at scope [0]
|
||||
>>> get_definition_scope_for_data_reference(spec, "data2", (0,))
|
||||
(0,)
|
||||
|
||||
If data2 is not visible at scope [] (the top level),
|
||||
because it's defined in scope [0]
|
||||
>>> repr(get_definition_scope_for_data_reference(spec, "data2", ()))
|
||||
'None'
|
||||
"""
|
||||
for i in reversed(range(len(usage_scope) + 1)):
|
||||
scope = usage_scope[:i]
|
||||
datasets = get_datasets_for_scope(vega_spec, scope)
|
||||
if data_name in datasets:
|
||||
return scope
|
||||
return None
|
||||
|
||||
|
||||
def get_facet_mapping(group: dict[str, Any], scope: Scope = ()) -> FacetMapping:
|
||||
"""
|
||||
Create mapping from facet definitions to source datasets.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : dict
|
||||
Top-level Vega spec or nested group mark
|
||||
scope : tuple of int
|
||||
Scope of the group dictionary within a top-level Vega spec
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Dictionary from (facet_name, facet_scope) to (dataset_name, dataset_scope)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> spec = {
|
||||
... "data": [{"name": "data1"}],
|
||||
... "marks": [
|
||||
... {
|
||||
... "type": "group",
|
||||
... "from": {
|
||||
... "facet": {
|
||||
... "name": "facet1",
|
||||
... "data": "data1",
|
||||
... "groupby": ["colA"],
|
||||
... }
|
||||
... },
|
||||
... }
|
||||
... ],
|
||||
... }
|
||||
>>> get_facet_mapping(spec)
|
||||
{('facet1', (0,)): ('data1', ())}
|
||||
"""
|
||||
facet_mapping = {}
|
||||
group_index = 0
|
||||
mark_group = get_group_mark_for_scope(group, scope) or {}
|
||||
for mark in mark_group.get("marks", []):
|
||||
if mark.get("type", None) == "group":
|
||||
# Get facet for this group
|
||||
group_scope = (*scope, group_index)
|
||||
facet = mark.get("from", {}).get("facet", None)
|
||||
if facet is not None:
|
||||
facet_name = facet.get("name", None)
|
||||
facet_data = facet.get("data", None)
|
||||
if facet_name is not None and facet_data is not None:
|
||||
definition_scope = get_definition_scope_for_data_reference(
|
||||
group, facet_data, scope
|
||||
)
|
||||
if definition_scope is not None:
|
||||
facet_mapping[facet_name, group_scope] = (
|
||||
facet_data,
|
||||
definition_scope,
|
||||
)
|
||||
|
||||
# Handle children recursively
|
||||
child_mapping = get_facet_mapping(group, scope=group_scope)
|
||||
facet_mapping.update(child_mapping)
|
||||
group_index += 1
|
||||
|
||||
return facet_mapping
|
||||
|
||||
|
||||
def get_from_facet_mapping(
|
||||
scoped_dataset: tuple[str, Scope], facet_mapping: FacetMapping
|
||||
) -> tuple[str, Scope]:
|
||||
"""
|
||||
Apply facet mapping to a scoped dataset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
scoped_dataset : (str, tuple of int)
|
||||
A dataset name and scope tuple
|
||||
facet_mapping : dict from (str, tuple of int) to (str, tuple of int)
|
||||
The facet mapping produced by get_facet_mapping
|
||||
|
||||
Returns
|
||||
-------
|
||||
(str, tuple of int)
|
||||
Dataset name and scope tuple that has been mapped as many times as possible
|
||||
|
||||
Examples
|
||||
--------
|
||||
Facet mapping as produced by get_facet_mapping
|
||||
>>> facet_mapping = {
|
||||
... ("facet1", (0,)): ("data1", ()),
|
||||
... ("facet2", (0, 1)): ("facet1", (0,)),
|
||||
... }
|
||||
>>> get_from_facet_mapping(("facet2", (0, 1)), facet_mapping)
|
||||
('data1', ())
|
||||
"""
|
||||
while scoped_dataset in facet_mapping:
|
||||
scoped_dataset = facet_mapping[scoped_dataset]
|
||||
return scoped_dataset
|
||||
|
||||
|
||||
def get_datasets_for_view_names(
|
||||
group: dict[str, Any],
|
||||
vl_chart_names: list[str],
|
||||
facet_mapping: FacetMapping,
|
||||
scope: Scope = (),
|
||||
) -> dict[str, tuple[str, Scope]]:
|
||||
"""
|
||||
Get the Vega datasets that correspond to the provided Altair view names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : dict
|
||||
Top-level Vega spec or nested group mark
|
||||
vl_chart_names : list of str
|
||||
List of the Vega-Lite
|
||||
facet_mapping : dict from (str, tuple of int) to (str, tuple of int)
|
||||
The facet mapping produced by get_facet_mapping
|
||||
scope : tuple of int
|
||||
Scope of the group dictionary within a top-level Vega spec
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict from str to (str, tuple of int)
|
||||
Dict from Altair view names to scoped datasets
|
||||
"""
|
||||
datasets = {}
|
||||
group_index = 0
|
||||
mark_group = get_group_mark_for_scope(group, scope) or {}
|
||||
for mark in mark_group.get("marks", []):
|
||||
for vl_chart_name in vl_chart_names:
|
||||
if mark.get("name", "") == f"{vl_chart_name}_cell":
|
||||
data_name = mark.get("from", {}).get("facet", None).get("data", None)
|
||||
scoped_data_name = (data_name, scope)
|
||||
datasets[vl_chart_name] = get_from_facet_mapping(
|
||||
scoped_data_name, facet_mapping
|
||||
)
|
||||
break
|
||||
|
||||
name = mark.get("name", "")
|
||||
if mark.get("type", "") == "group":
|
||||
group_data_names = get_datasets_for_view_names(
|
||||
group, vl_chart_names, facet_mapping, scope=(*scope, group_index)
|
||||
)
|
||||
for k, v in group_data_names.items():
|
||||
datasets.setdefault(k, v)
|
||||
group_index += 1
|
||||
else:
|
||||
for vl_chart_name in vl_chart_names:
|
||||
if name.startswith(vl_chart_name) and name.endswith("_marks"):
|
||||
data_name = mark.get("from", {}).get("data", None)
|
||||
scoped_data = get_definition_scope_for_data_reference(
|
||||
group, data_name, scope
|
||||
)
|
||||
if scoped_data is not None:
|
||||
datasets[vl_chart_name] = get_from_facet_mapping(
|
||||
(data_name, scoped_data), facet_mapping
|
||||
)
|
||||
break
|
||||
|
||||
return datasets
|
||||
@@ -0,0 +1,304 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from importlib.metadata import version as importlib_version
|
||||
from typing import TYPE_CHECKING, Any, Callable, Final, TypedDict, Union, overload
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from narwhals.stable.v1.dependencies import is_into_dataframe
|
||||
from packaging.version import Version
|
||||
|
||||
from altair.utils._importers import import_vegafusion
|
||||
from altair.utils.core import DataFrameLike
|
||||
from altair.utils.data import (
|
||||
DataType,
|
||||
MaxRowsError,
|
||||
SupportsGeoInterface,
|
||||
ToValuesReturnType,
|
||||
)
|
||||
from altair.vegalite.data import default_data_transformer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
from collections.abc import MutableMapping
|
||||
|
||||
from narwhals.stable.v1.typing import IntoDataFrame
|
||||
|
||||
from vegafusion.runtime import ChartState
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeIs
|
||||
else:
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
# Temporary storage for dataframes that have been extracted
|
||||
# from charts by the vegafusion data transformer. Use a WeakValueDictionary
|
||||
# rather than a dict so that the Python interpreter is free to garbage
|
||||
# collect the stored DataFrames.
|
||||
extracted_inline_tables: MutableMapping[str, DataFrameLike] = WeakValueDictionary()
|
||||
|
||||
# Special URL prefix that VegaFusion uses to denote that a
|
||||
# dataset in a Vega spec corresponds to an entry in the `inline_datasets`
|
||||
# kwarg of vf.runtime.pre_transform_spec().
|
||||
VEGAFUSION_PREFIX: Final = "vegafusion+dataset://"
|
||||
|
||||
|
||||
try:
|
||||
VEGAFUSION_VERSION: Version | None = Version(importlib_version("vegafusion"))
|
||||
except ImportError:
|
||||
VEGAFUSION_VERSION = None
|
||||
|
||||
|
||||
if VEGAFUSION_VERSION and Version("2.0.0a0") <= VEGAFUSION_VERSION:
|
||||
|
||||
def is_supported_by_vf(data: Any) -> TypeIs[DataFrameLike]:
|
||||
# Test whether VegaFusion supports the data type
|
||||
# VegaFusion v2 support narwhals-compatible DataFrames
|
||||
return isinstance(data, DataFrameLike) or is_into_dataframe(data)
|
||||
|
||||
else:
|
||||
|
||||
def is_supported_by_vf(data: Any) -> TypeIs[DataFrameLike]:
|
||||
return isinstance(data, DataFrameLike)
|
||||
|
||||
|
||||
class _ToVegaFusionReturnUrlDict(TypedDict):
|
||||
url: str
|
||||
|
||||
|
||||
_VegaFusionReturnType = Union[_ToVegaFusionReturnUrlDict, ToValuesReturnType]
|
||||
|
||||
|
||||
@overload
|
||||
def vegafusion_data_transformer(
|
||||
data: None = ..., max_rows: int = ...
|
||||
) -> Callable[..., Any]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def vegafusion_data_transformer(
|
||||
data: DataFrameLike, max_rows: int = ...
|
||||
) -> ToValuesReturnType: ...
|
||||
|
||||
|
||||
@overload
|
||||
def vegafusion_data_transformer(
|
||||
data: dict | IntoDataFrame | SupportsGeoInterface, max_rows: int = ...
|
||||
) -> _VegaFusionReturnType: ...
|
||||
|
||||
|
||||
def vegafusion_data_transformer(
|
||||
data: DataType | None = None, max_rows: int = 100000
|
||||
) -> Callable[..., Any] | _VegaFusionReturnType:
|
||||
"""VegaFusion Data Transformer."""
|
||||
if data is None:
|
||||
return vegafusion_data_transformer
|
||||
|
||||
if is_supported_by_vf(data) and not isinstance(data, SupportsGeoInterface):
|
||||
table_name = f"table_{uuid.uuid4()}".replace("-", "_")
|
||||
extracted_inline_tables[table_name] = data
|
||||
return {"url": VEGAFUSION_PREFIX + table_name}
|
||||
else:
|
||||
# Use default transformer for geo interface objects
|
||||
# # (e.g. a geopandas GeoDataFrame)
|
||||
# Or if we don't recognize data type
|
||||
return default_data_transformer(data)
|
||||
|
||||
|
||||
def get_inline_table_names(vega_spec: dict[str, Any]) -> set[str]:
|
||||
"""
|
||||
Get a set of the inline datasets names in the provided Vega spec.
|
||||
|
||||
Inline datasets are encoded as URLs that start with the table://
|
||||
prefix.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vega_spec: dict
|
||||
A Vega specification dict
|
||||
|
||||
Returns
|
||||
-------
|
||||
set of str
|
||||
Set of the names of the inline datasets that are referenced
|
||||
in the specification.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> spec = {
|
||||
... "data": [
|
||||
... {"name": "foo", "url": "https://path/to/file.csv"},
|
||||
... {"name": "bar", "url": "vegafusion+dataset://inline_dataset_123"},
|
||||
... ]
|
||||
... }
|
||||
>>> get_inline_table_names(spec)
|
||||
{'inline_dataset_123'}
|
||||
"""
|
||||
table_names = set()
|
||||
|
||||
# Process datasets
|
||||
for data in vega_spec.get("data", []):
|
||||
url = data.get("url", "")
|
||||
if url.startswith(VEGAFUSION_PREFIX):
|
||||
name = url[len(VEGAFUSION_PREFIX) :]
|
||||
table_names.add(name)
|
||||
|
||||
# Recursively process child marks, which may have their own datasets
|
||||
for mark in vega_spec.get("marks", []):
|
||||
table_names.update(get_inline_table_names(mark))
|
||||
|
||||
return table_names
|
||||
|
||||
|
||||
def get_inline_tables(vega_spec: dict[str, Any]) -> dict[str, DataFrameLike]:
|
||||
"""
|
||||
Get the inline tables referenced by a Vega specification.
|
||||
|
||||
Note: This function should only be called on a Vega spec that corresponds
|
||||
to a chart that was processed by the vegafusion_data_transformer.
|
||||
Furthermore, this function may only be called once per spec because
|
||||
the returned dataframes are deleted from internal storage.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vega_spec: dict
|
||||
A Vega specification dict
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict from str to dataframe
|
||||
dict from inline dataset name to dataframe object
|
||||
"""
|
||||
inline_names = get_inline_table_names(vega_spec)
|
||||
# exclude named dataset that was provided by the user,
|
||||
# or dataframes that have been deleted.
|
||||
table_names = inline_names.intersection(extracted_inline_tables)
|
||||
return {k: extracted_inline_tables.pop(k) for k in table_names}
|
||||
|
||||
|
||||
def compile_to_vegafusion_chart_state(
|
||||
vegalite_spec: dict[str, Any], local_tz: str
|
||||
) -> ChartState:
|
||||
"""
|
||||
Compile a Vega-Lite spec to a VegaFusion ChartState.
|
||||
|
||||
Note: This function should only be called on a Vega-Lite spec
|
||||
that was generated with the "vegafusion" data transformer enabled.
|
||||
In particular, this spec may contain references to extract datasets
|
||||
using table:// prefixed URLs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vegalite_spec: dict
|
||||
A Vega-Lite spec that was generated from an Altair chart with
|
||||
the "vegafusion" data transformer enabled
|
||||
local_tz: str
|
||||
Local timezone name (e.g. 'America/New_York')
|
||||
|
||||
Returns
|
||||
-------
|
||||
ChartState
|
||||
A VegaFusion ChartState object
|
||||
"""
|
||||
# Local import to avoid circular ImportError
|
||||
from altair import data_transformers, vegalite_compilers
|
||||
|
||||
vf = import_vegafusion()
|
||||
|
||||
# Compile Vega-Lite spec to Vega
|
||||
compiler = vegalite_compilers.get()
|
||||
if compiler is None:
|
||||
msg = "No active vega-lite compiler plugin found"
|
||||
raise ValueError(msg)
|
||||
|
||||
vega_spec = compiler(vegalite_spec)
|
||||
|
||||
# Retrieve dict of inline tables referenced by the spec
|
||||
inline_tables = get_inline_tables(vega_spec)
|
||||
|
||||
# Pre-evaluate transforms in vega spec with vegafusion
|
||||
row_limit = data_transformers.options.get("max_rows", None)
|
||||
|
||||
chart_state = vf.runtime.new_chart_state(
|
||||
vega_spec,
|
||||
local_tz=local_tz,
|
||||
inline_datasets=inline_tables,
|
||||
row_limit=row_limit,
|
||||
)
|
||||
|
||||
# Check from row limit warning and convert to MaxRowsError
|
||||
handle_row_limit_exceeded(row_limit, chart_state.get_warnings())
|
||||
|
||||
return chart_state
|
||||
|
||||
|
||||
def compile_with_vegafusion(vegalite_spec: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Compile a Vega-Lite spec to Vega and pre-transform with VegaFusion.
|
||||
|
||||
Note: This function should only be called on a Vega-Lite spec
|
||||
that was generated with the "vegafusion" data transformer enabled.
|
||||
In particular, this spec may contain references to extract datasets
|
||||
using table:// prefixed URLs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vegalite_spec: dict
|
||||
A Vega-Lite spec that was generated from an Altair chart with
|
||||
the "vegafusion" data transformer enabled
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
A Vega spec that has been pre-transformed by VegaFusion
|
||||
"""
|
||||
# Local import to avoid circular ImportError
|
||||
from altair import data_transformers, vegalite_compilers
|
||||
|
||||
vf = import_vegafusion()
|
||||
|
||||
# Compile Vega-Lite spec to Vega
|
||||
compiler = vegalite_compilers.get()
|
||||
if compiler is None:
|
||||
msg = "No active vega-lite compiler plugin found"
|
||||
raise ValueError(msg)
|
||||
|
||||
vega_spec = compiler(vegalite_spec)
|
||||
|
||||
# Retrieve dict of inline tables referenced by the spec
|
||||
inline_tables = get_inline_tables(vega_spec)
|
||||
|
||||
# Pre-evaluate transforms in vega spec with vegafusion
|
||||
row_limit = data_transformers.options.get("max_rows", None)
|
||||
transformed_vega_spec, warnings = vf.runtime.pre_transform_spec(
|
||||
vega_spec,
|
||||
vf.get_local_tz(),
|
||||
inline_datasets=inline_tables,
|
||||
row_limit=row_limit,
|
||||
)
|
||||
|
||||
# Check from row limit warning and convert to MaxRowsError
|
||||
handle_row_limit_exceeded(row_limit, warnings)
|
||||
|
||||
return transformed_vega_spec
|
||||
|
||||
|
||||
def handle_row_limit_exceeded(row_limit: int, warnings: list):
|
||||
for warning in warnings:
|
||||
if warning.get("type") == "RowLimitExceeded":
|
||||
msg = (
|
||||
"The number of dataset rows after filtering and aggregation exceeds\n"
|
||||
f"the current limit of {row_limit}. Try adding an aggregation to reduce\n"
|
||||
"the size of the dataset that must be loaded into the browser. Or, disable\n"
|
||||
"the limit by calling alt.data_transformers.disable_max_rows(). Note that\n"
|
||||
"disabling this limit may cause the browser to freeze or crash."
|
||||
)
|
||||
raise MaxRowsError(msg)
|
||||
|
||||
|
||||
def using_vegafusion() -> bool:
|
||||
"""Check whether the vegafusion data transformer is enabled."""
|
||||
# Local import to avoid circular ImportError
|
||||
from altair import data_transformers
|
||||
|
||||
return data_transformers.active == "vegafusion"
|
||||
12
myenv/lib/python3.11/site-packages/altair/utils/compiler.py
Normal file
12
myenv/lib/python3.11/site-packages/altair/utils/compiler.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from typing import Any, Callable
|
||||
|
||||
from altair.utils import PluginRegistry
|
||||
|
||||
# ==============================================================================
|
||||
# Vega-Lite to Vega compiler registry
|
||||
# ==============================================================================
|
||||
VegaLiteCompilerType = Callable[[dict[str, Any]], dict[str, Any]]
|
||||
|
||||
|
||||
class VegaLiteCompilerRegistry(PluginRegistry[VegaLiteCompilerType, dict[str, Any]]):
|
||||
pass
|
||||
981
myenv/lib/python3.11/site-packages/altair/utils/core.py
Normal file
981
myenv/lib/python3.11/site-packages/altair/utils/core.py
Normal file
@@ -0,0 +1,981 @@
|
||||
"""Utility routines."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import warnings
|
||||
from collections.abc import Iterator, Mapping, MutableMapping
|
||||
from copy import deepcopy
|
||||
from itertools import groupby
|
||||
from operator import itemgetter
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, cast, overload
|
||||
|
||||
import jsonschema
|
||||
import narwhals.stable.v1 as nw
|
||||
from narwhals.stable.v1.dependencies import is_pandas_dataframe, is_polars_dataframe
|
||||
from narwhals.stable.v1.typing import IntoDataFrame
|
||||
|
||||
from altair.utils.schemapi import SchemaBase, SchemaLike, Undefined
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
from typing import Protocol, TypeAliasType, runtime_checkable
|
||||
else:
|
||||
from typing_extensions import Protocol, TypeAliasType, runtime_checkable
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import Concatenate, ParamSpec
|
||||
else:
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import typing as t
|
||||
|
||||
import pandas as pd
|
||||
from narwhals.stable.v1.typing import IntoExpr
|
||||
|
||||
from altair.utils._dfi_types import DataFrame as DfiDataFrame
|
||||
from altair.vegalite.v5.schema._typing import StandardType_T as InferredVegaLiteType
|
||||
|
||||
TIntoDataFrame = TypeVar("TIntoDataFrame", bound=IntoDataFrame)
|
||||
T = TypeVar("T")
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
WrapsFunc = TypeAliasType("WrapsFunc", Callable[..., R], type_params=(R,))
|
||||
WrappedFunc = TypeAliasType("WrappedFunc", Callable[P, R], type_params=(P, R))
|
||||
# NOTE: Requires stringized form to avoid `< (3, 11)` issues
|
||||
# See: https://github.com/vega/altair/actions/runs/10667859416/job/29567290871?pr=3565
|
||||
WrapsMethod = TypeAliasType(
|
||||
"WrapsMethod", "Callable[Concatenate[T, ...], R]", type_params=(T, R)
|
||||
)
|
||||
WrappedMethod = TypeAliasType(
|
||||
"WrappedMethod", Callable[Concatenate[T, P], R], type_params=(T, P, R)
|
||||
)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class DataFrameLike(Protocol):
|
||||
def __dataframe__(
|
||||
self, nan_as_null: bool = False, allow_copy: bool = True
|
||||
) -> DfiDataFrame: ...
|
||||
|
||||
|
||||
TYPECODE_MAP = {
|
||||
"ordinal": "O",
|
||||
"nominal": "N",
|
||||
"quantitative": "Q",
|
||||
"temporal": "T",
|
||||
"geojson": "G",
|
||||
}
|
||||
|
||||
INV_TYPECODE_MAP = {v: k for k, v in TYPECODE_MAP.items()}
|
||||
|
||||
|
||||
# aggregates from vega-lite version 4.6.0
|
||||
AGGREGATES = [
|
||||
"argmax",
|
||||
"argmin",
|
||||
"average",
|
||||
"count",
|
||||
"distinct",
|
||||
"max",
|
||||
"mean",
|
||||
"median",
|
||||
"min",
|
||||
"missing",
|
||||
"product",
|
||||
"q1",
|
||||
"q3",
|
||||
"ci0",
|
||||
"ci1",
|
||||
"stderr",
|
||||
"stdev",
|
||||
"stdevp",
|
||||
"sum",
|
||||
"valid",
|
||||
"values",
|
||||
"variance",
|
||||
"variancep",
|
||||
"exponential",
|
||||
"exponentialb",
|
||||
]
|
||||
|
||||
# window aggregates from vega-lite version 4.6.0
|
||||
WINDOW_AGGREGATES = [
|
||||
"row_number",
|
||||
"rank",
|
||||
"dense_rank",
|
||||
"percent_rank",
|
||||
"cume_dist",
|
||||
"ntile",
|
||||
"lag",
|
||||
"lead",
|
||||
"first_value",
|
||||
"last_value",
|
||||
"nth_value",
|
||||
]
|
||||
|
||||
# timeUnits from vega-lite version 4.17.0
|
||||
TIMEUNITS = [
|
||||
"year",
|
||||
"quarter",
|
||||
"month",
|
||||
"week",
|
||||
"day",
|
||||
"dayofyear",
|
||||
"date",
|
||||
"hours",
|
||||
"minutes",
|
||||
"seconds",
|
||||
"milliseconds",
|
||||
"yearquarter",
|
||||
"yearquartermonth",
|
||||
"yearmonth",
|
||||
"yearmonthdate",
|
||||
"yearmonthdatehours",
|
||||
"yearmonthdatehoursminutes",
|
||||
"yearmonthdatehoursminutesseconds",
|
||||
"yearweek",
|
||||
"yearweekday",
|
||||
"yearweekdayhours",
|
||||
"yearweekdayhoursminutes",
|
||||
"yearweekdayhoursminutesseconds",
|
||||
"yeardayofyear",
|
||||
"quartermonth",
|
||||
"monthdate",
|
||||
"monthdatehours",
|
||||
"monthdatehoursminutes",
|
||||
"monthdatehoursminutesseconds",
|
||||
"weekday",
|
||||
"weeksdayhours",
|
||||
"weekdayhours",
|
||||
"weekdayhoursminutes",
|
||||
"weekdayhoursminutesseconds",
|
||||
"dayhours",
|
||||
"dayhoursminutes",
|
||||
"dayhoursminutesseconds",
|
||||
"hoursminutes",
|
||||
"hoursminutesseconds",
|
||||
"minutesseconds",
|
||||
"secondsmilliseconds",
|
||||
"utcyear",
|
||||
"utcquarter",
|
||||
"utcmonth",
|
||||
"utcweek",
|
||||
"utcday",
|
||||
"utcdayofyear",
|
||||
"utcdate",
|
||||
"utchours",
|
||||
"utcminutes",
|
||||
"utcseconds",
|
||||
"utcmilliseconds",
|
||||
"utcyearquarter",
|
||||
"utcyearquartermonth",
|
||||
"utcyearmonth",
|
||||
"utcyearmonthdate",
|
||||
"utcyearmonthdatehours",
|
||||
"utcyearmonthdatehoursminutes",
|
||||
"utcyearmonthdatehoursminutesseconds",
|
||||
"utcyearweek",
|
||||
"utcyearweekday",
|
||||
"utcyearweekdayhours",
|
||||
"utcyearweekdayhoursminutes",
|
||||
"utcyearweekdayhoursminutesseconds",
|
||||
"utcyeardayofyear",
|
||||
"utcquartermonth",
|
||||
"utcmonthdate",
|
||||
"utcmonthdatehours",
|
||||
"utcmonthdatehoursminutes",
|
||||
"utcmonthdatehoursminutesseconds",
|
||||
"utcweekday",
|
||||
"utcweekdayhours",
|
||||
"utcweekdayhoursminutes",
|
||||
"utcweekdayhoursminutesseconds",
|
||||
"utcdayhours",
|
||||
"utcdayhoursminutes",
|
||||
"utcdayhoursminutesseconds",
|
||||
"utchoursminutes",
|
||||
"utchoursminutesseconds",
|
||||
"utcminutesseconds",
|
||||
"utcsecondsmilliseconds",
|
||||
]
|
||||
|
||||
VALID_TYPECODES = list(itertools.chain(iter(TYPECODE_MAP), iter(INV_TYPECODE_MAP)))
|
||||
|
||||
SHORTHAND_UNITS = {
|
||||
"field": "(?P<field>.*)",
|
||||
"type": "(?P<type>{})".format("|".join(VALID_TYPECODES)),
|
||||
"agg_count": "(?P<aggregate>count)",
|
||||
"op_count": "(?P<op>count)",
|
||||
"aggregate": "(?P<aggregate>{})".format("|".join(AGGREGATES)),
|
||||
"window_op": "(?P<op>{})".format("|".join(AGGREGATES + WINDOW_AGGREGATES)),
|
||||
"timeUnit": "(?P<timeUnit>{})".format("|".join(TIMEUNITS)),
|
||||
}
|
||||
|
||||
SHORTHAND_KEYS: frozenset[Literal["field", "aggregate", "type", "timeUnit"]] = (
|
||||
frozenset(("field", "aggregate", "type", "timeUnit"))
|
||||
)
|
||||
|
||||
|
||||
def infer_vegalite_type_for_pandas(
|
||||
data: Any,
|
||||
) -> InferredVegaLiteType | tuple[InferredVegaLiteType, list[Any]]:
|
||||
"""
|
||||
From an array-like input, infer the correct vega typecode.
|
||||
|
||||
('ordinal', 'nominal', 'quantitative', or 'temporal').
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data: Any
|
||||
"""
|
||||
# This is safe to import here, as this function is only called on pandas input.
|
||||
from pandas.api.types import infer_dtype
|
||||
|
||||
typ = infer_dtype(data, skipna=False)
|
||||
|
||||
if typ in {
|
||||
"floating",
|
||||
"mixed-integer-float",
|
||||
"integer",
|
||||
"mixed-integer",
|
||||
"complex",
|
||||
}:
|
||||
return "quantitative"
|
||||
elif typ == "categorical" and hasattr(data, "cat") and data.cat.ordered:
|
||||
return ("ordinal", data.cat.categories.tolist())
|
||||
elif typ in {"string", "bytes", "categorical", "boolean", "mixed", "unicode"}:
|
||||
return "nominal"
|
||||
elif typ in {
|
||||
"datetime",
|
||||
"datetime64",
|
||||
"timedelta",
|
||||
"timedelta64",
|
||||
"date",
|
||||
"time",
|
||||
"period",
|
||||
}:
|
||||
return "temporal"
|
||||
else:
|
||||
warnings.warn(
|
||||
f"I don't know how to infer vegalite type from '{typ}'. "
|
||||
"Defaulting to nominal.",
|
||||
stacklevel=1,
|
||||
)
|
||||
return "nominal"
|
||||
|
||||
|
||||
def merge_props_geom(feat: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Merge properties with geometry.
|
||||
|
||||
* Overwrites 'type' and 'geometry' entries if existing.
|
||||
"""
|
||||
geom = {k: feat[k] for k in ("type", "geometry")}
|
||||
try:
|
||||
feat["properties"].update(geom)
|
||||
props_geom = feat["properties"]
|
||||
except (AttributeError, KeyError):
|
||||
# AttributeError when 'properties' equals None
|
||||
# KeyError when 'properties' is non-existing
|
||||
props_geom = geom
|
||||
|
||||
return props_geom
|
||||
|
||||
|
||||
def sanitize_geo_interface(geo: t.MutableMapping[Any, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Santize a geo_interface to prepare it for serialization.
|
||||
|
||||
* Make a copy
|
||||
* Convert type array or _Array to list
|
||||
* Convert tuples to lists (using json.loads/dumps)
|
||||
* Merge properties with geometry
|
||||
"""
|
||||
geo = deepcopy(geo)
|
||||
|
||||
# convert type _Array or array to list
|
||||
for key in geo:
|
||||
if str(type(geo[key]).__name__).startswith(("_Array", "array")):
|
||||
geo[key] = geo[key].tolist()
|
||||
|
||||
# convert (nested) tuples to lists
|
||||
geo_dct: dict = json.loads(json.dumps(geo))
|
||||
|
||||
# sanitize features
|
||||
if geo_dct["type"] == "FeatureCollection":
|
||||
geo_dct = geo_dct["features"]
|
||||
if len(geo_dct) > 0:
|
||||
for idx, feat in enumerate(geo_dct):
|
||||
geo_dct[idx] = merge_props_geom(feat)
|
||||
elif geo_dct["type"] == "Feature":
|
||||
geo_dct = merge_props_geom(geo_dct)
|
||||
else:
|
||||
geo_dct = {"type": "Feature", "geometry": geo_dct}
|
||||
|
||||
return geo_dct
|
||||
|
||||
|
||||
def numpy_is_subtype(dtype: Any, subtype: Any) -> bool:
|
||||
# This is only called on `numpy` inputs, so it's safe to import it here.
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
return np.issubdtype(dtype, subtype)
|
||||
except (NotImplementedError, TypeError):
|
||||
return False
|
||||
|
||||
|
||||
def sanitize_pandas_dataframe(df: pd.DataFrame) -> pd.DataFrame: # noqa: C901
|
||||
"""
|
||||
Sanitize a DataFrame to prepare it for serialization.
|
||||
|
||||
* Make a copy
|
||||
* Convert RangeIndex columns to strings
|
||||
* Raise ValueError if column names are not strings
|
||||
* Raise ValueError if it has a hierarchical index.
|
||||
* Convert categoricals to strings.
|
||||
* Convert np.bool_ dtypes to Python bool objects
|
||||
* Convert np.int dtypes to Python int objects
|
||||
* Convert floats to objects and replace NaNs/infs with None.
|
||||
* Convert DateTime dtypes into appropriate string representations
|
||||
* Convert Nullable integers to objects and replace NaN with None
|
||||
* Convert Nullable boolean to objects and replace NaN with None
|
||||
* convert dedicated string column to objects and replace NaN with None
|
||||
* Raise a ValueError for TimeDelta dtypes
|
||||
"""
|
||||
# This is safe to import here, as this function is only called on pandas input.
|
||||
# NumPy is a required dependency of pandas so is also safe to import.
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
df = df.copy()
|
||||
|
||||
if isinstance(df.columns, pd.RangeIndex):
|
||||
df.columns = df.columns.astype(str)
|
||||
|
||||
for col_name in df.columns:
|
||||
if not isinstance(col_name, str):
|
||||
msg = (
|
||||
f"Dataframe contains invalid column name: {col_name!r}. "
|
||||
"Column names must be strings"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
if isinstance(df.index, pd.MultiIndex):
|
||||
msg = "Hierarchical indices not supported"
|
||||
raise ValueError(msg)
|
||||
if isinstance(df.columns, pd.MultiIndex):
|
||||
msg = "Hierarchical indices not supported"
|
||||
raise ValueError(msg)
|
||||
|
||||
def to_list_if_array(val):
|
||||
if isinstance(val, np.ndarray):
|
||||
return val.tolist()
|
||||
else:
|
||||
return val
|
||||
|
||||
for dtype_item in df.dtypes.items():
|
||||
# We know that the column names are strings from the isinstance check
|
||||
# further above but mypy thinks it is of type Hashable and therefore does not
|
||||
# let us assign it to the col_name variable which is already of type str.
|
||||
col_name = cast(str, dtype_item[0])
|
||||
dtype = dtype_item[1]
|
||||
dtype_name = str(dtype)
|
||||
if dtype_name == "category":
|
||||
# Work around bug in to_json for categorical types in older versions
|
||||
# of pandas as they do not properly convert NaN values to null in to_json.
|
||||
# We can probably remove this part once we require pandas >= 1.0
|
||||
col = df[col_name].astype(object)
|
||||
df[col_name] = col.where(col.notnull(), None)
|
||||
elif dtype_name == "string":
|
||||
# dedicated string datatype (since 1.0)
|
||||
# https://pandas.pydata.org/pandas-docs/version/1.0.0/whatsnew/v1.0.0.html#dedicated-string-data-type
|
||||
col = df[col_name].astype(object)
|
||||
df[col_name] = col.where(col.notnull(), None)
|
||||
elif dtype_name == "bool":
|
||||
# convert numpy bools to objects; np.bool is not JSON serializable
|
||||
df[col_name] = df[col_name].astype(object)
|
||||
elif dtype_name == "boolean":
|
||||
# dedicated boolean datatype (since 1.0)
|
||||
# https://pandas.io/docs/user_guide/boolean.html
|
||||
col = df[col_name].astype(object)
|
||||
df[col_name] = col.where(col.notnull(), None)
|
||||
elif dtype_name.startswith(("datetime", "timestamp")):
|
||||
# Convert datetimes to strings. This needs to be a full ISO string
|
||||
# with time, which is why we cannot use ``col.astype(str)``.
|
||||
# This is because Javascript parses date-only times in UTC, but
|
||||
# parses full ISO-8601 dates as local time, and dates in Vega and
|
||||
# Vega-Lite are displayed in local time by default.
|
||||
# (see https://github.com/vega/altair/issues/1027)
|
||||
df[col_name] = (
|
||||
df[col_name].apply(lambda x: x.isoformat()).replace("NaT", "")
|
||||
)
|
||||
elif dtype_name.startswith("timedelta"):
|
||||
msg = (
|
||||
f'Field "{col_name}" has type "{dtype}" which is '
|
||||
"not supported by Altair. Please convert to "
|
||||
"either a timestamp or a numerical value."
|
||||
""
|
||||
)
|
||||
raise ValueError(msg)
|
||||
elif dtype_name.startswith("geometry"):
|
||||
# geopandas >=0.6.1 uses the dtype geometry. Continue here
|
||||
# otherwise it will give an error on np.issubdtype(dtype, np.integer)
|
||||
continue
|
||||
elif (
|
||||
dtype_name
|
||||
in {
|
||||
"Int8",
|
||||
"Int16",
|
||||
"Int32",
|
||||
"Int64",
|
||||
"UInt8",
|
||||
"UInt16",
|
||||
"UInt32",
|
||||
"UInt64",
|
||||
"Float32",
|
||||
"Float64",
|
||||
}
|
||||
): # nullable integer datatypes (since 24.0) and nullable float datatypes (since 1.2.0)
|
||||
# https://pandas.pydata.org/pandas-docs/version/0.25/whatsnew/v0.24.0.html#optional-integer-na-support
|
||||
col = df[col_name].astype(object)
|
||||
df[col_name] = col.where(col.notnull(), None)
|
||||
elif numpy_is_subtype(dtype, np.integer):
|
||||
# convert integers to objects; np.int is not JSON serializable
|
||||
df[col_name] = df[col_name].astype(object)
|
||||
elif numpy_is_subtype(dtype, np.floating):
|
||||
# For floats, convert to Python float: np.float is not JSON serializable
|
||||
# Also convert NaN/inf values to null, as they are not JSON serializable
|
||||
col = df[col_name]
|
||||
bad_values = col.isnull() | np.isinf(col)
|
||||
df[col_name] = col.astype(object).where(~bad_values, None)
|
||||
elif dtype == object: # noqa: E721
|
||||
# Convert numpy arrays saved as objects to lists
|
||||
# Arrays are not JSON serializable
|
||||
col = df[col_name].astype(object).apply(to_list_if_array)
|
||||
df[col_name] = col.where(col.notnull(), None)
|
||||
return df
|
||||
|
||||
|
||||
def sanitize_narwhals_dataframe(
|
||||
data: nw.DataFrame[TIntoDataFrame],
|
||||
) -> nw.DataFrame[TIntoDataFrame]:
|
||||
"""Sanitize narwhals.DataFrame for JSON serialization."""
|
||||
schema = data.schema
|
||||
columns: list[IntoExpr] = []
|
||||
# See https://github.com/vega/altair/issues/1027 for why this is necessary.
|
||||
local_iso_fmt_string = "%Y-%m-%dT%H:%M:%S"
|
||||
is_polars = is_polars_dataframe(data.to_native())
|
||||
for name, dtype in schema.items():
|
||||
if dtype == nw.Date and is_polars:
|
||||
# Polars doesn't allow formatting `Date` with time directives.
|
||||
# The date -> datetime cast is extremely fast compared with `to_string`
|
||||
columns.append(
|
||||
nw.col(name).cast(nw.Datetime).dt.to_string(local_iso_fmt_string)
|
||||
)
|
||||
elif dtype == nw.Date:
|
||||
columns.append(nw.col(name).dt.to_string(local_iso_fmt_string))
|
||||
elif dtype == nw.Datetime:
|
||||
columns.append(nw.col(name).dt.to_string(f"{local_iso_fmt_string}%.f"))
|
||||
elif dtype == nw.Duration:
|
||||
msg = (
|
||||
f'Field "{name}" has type "{dtype}" which is '
|
||||
"not supported by Altair. Please convert to "
|
||||
"either a timestamp or a numerical value."
|
||||
""
|
||||
)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
columns.append(name)
|
||||
return data.select(columns)
|
||||
|
||||
|
||||
def to_eager_narwhals_dataframe(data: IntoDataFrame) -> nw.DataFrame[Any]:
|
||||
"""
|
||||
Wrap `data` in `narwhals.DataFrame`.
|
||||
|
||||
If `data` is not supported by Narwhals, but it is convertible
|
||||
to a PyArrow table, then first convert to a PyArrow Table,
|
||||
and then wrap in `narwhals.DataFrame`.
|
||||
"""
|
||||
data_nw = nw.from_native(data, eager_or_interchange_only=True)
|
||||
if nw.get_level(data_nw) == "interchange":
|
||||
# If Narwhals' support for `data`'s class is only metadata-level, then we
|
||||
# use the interchange protocol to convert to a PyArrow Table.
|
||||
from altair.utils.data import arrow_table_from_dfi_dataframe
|
||||
|
||||
pa_table = arrow_table_from_dfi_dataframe(data) # type: ignore[arg-type]
|
||||
data_nw = nw.from_native(pa_table, eager_only=True)
|
||||
return data_nw
|
||||
|
||||
|
||||
def parse_shorthand( # noqa: C901
|
||||
shorthand: dict[str, Any] | str,
|
||||
data: IntoDataFrame | None = None,
|
||||
parse_aggregates: bool = True,
|
||||
parse_window_ops: bool = False,
|
||||
parse_timeunits: bool = True,
|
||||
parse_types: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
General tool to parse shorthand values.
|
||||
|
||||
These are of the form:
|
||||
|
||||
- "col_name"
|
||||
- "col_name:O"
|
||||
- "average(col_name)"
|
||||
- "average(col_name):O"
|
||||
|
||||
Optionally, a dataframe may be supplied, from which the type
|
||||
will be inferred if not specified in the shorthand.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
shorthand : dict or string
|
||||
The shorthand representation to be parsed
|
||||
data : DataFrame, optional
|
||||
If specified and of type DataFrame, then use these values to infer the
|
||||
column type if not provided by the shorthand.
|
||||
parse_aggregates : boolean
|
||||
If True (default), then parse aggregate functions within the shorthand.
|
||||
parse_window_ops : boolean
|
||||
If True then parse window operations within the shorthand (default:False)
|
||||
parse_timeunits : boolean
|
||||
If True (default), then parse timeUnits from within the shorthand
|
||||
parse_types : boolean
|
||||
If True (default), then parse typecodes within the shorthand
|
||||
|
||||
Returns
|
||||
-------
|
||||
attrs : dict
|
||||
a dictionary of attributes extracted from the shorthand
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import pandas as pd
|
||||
>>> data = pd.DataFrame({"foo": ["A", "B", "A", "B"], "bar": [1, 2, 3, 4]})
|
||||
|
||||
>>> parse_shorthand("name") == {"field": "name"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("name:Q") == {"field": "name", "type": "quantitative"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("average(col)") == {"aggregate": "average", "field": "col"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("foo:O") == {"field": "foo", "type": "ordinal"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("min(foo):Q") == {
|
||||
... "aggregate": "min",
|
||||
... "field": "foo",
|
||||
... "type": "quantitative",
|
||||
... }
|
||||
True
|
||||
|
||||
>>> parse_shorthand("month(col)") == {
|
||||
... "field": "col",
|
||||
... "timeUnit": "month",
|
||||
... "type": "temporal",
|
||||
... }
|
||||
True
|
||||
|
||||
>>> parse_shorthand("year(col):O") == {
|
||||
... "field": "col",
|
||||
... "timeUnit": "year",
|
||||
... "type": "ordinal",
|
||||
... }
|
||||
True
|
||||
|
||||
>>> parse_shorthand("foo", data) == {"field": "foo", "type": "nominal"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("bar", data) == {"field": "bar", "type": "quantitative"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("bar:O", data) == {"field": "bar", "type": "ordinal"}
|
||||
True
|
||||
|
||||
>>> parse_shorthand("sum(bar)", data) == {
|
||||
... "aggregate": "sum",
|
||||
... "field": "bar",
|
||||
... "type": "quantitative",
|
||||
... }
|
||||
True
|
||||
|
||||
>>> parse_shorthand("count()", data) == {
|
||||
... "aggregate": "count",
|
||||
... "type": "quantitative",
|
||||
... }
|
||||
True
|
||||
"""
|
||||
from altair.utils.data import is_data_type
|
||||
|
||||
if not shorthand:
|
||||
return {}
|
||||
|
||||
patterns = []
|
||||
|
||||
if parse_aggregates:
|
||||
patterns.extend([r"{agg_count}\(\)"])
|
||||
patterns.extend([r"{aggregate}\({field}\)"])
|
||||
if parse_window_ops:
|
||||
patterns.extend([r"{op_count}\(\)"])
|
||||
patterns.extend([r"{window_op}\({field}\)"])
|
||||
if parse_timeunits:
|
||||
patterns.extend([r"{timeUnit}\({field}\)"])
|
||||
|
||||
patterns.extend([r"{field}"])
|
||||
|
||||
if parse_types:
|
||||
patterns = list(itertools.chain(*((p + ":{type}", p) for p in patterns)))
|
||||
|
||||
regexps = (
|
||||
re.compile(r"\A" + p.format(**SHORTHAND_UNITS) + r"\Z", re.DOTALL)
|
||||
for p in patterns
|
||||
)
|
||||
|
||||
# find matches depending on valid fields passed
|
||||
if isinstance(shorthand, dict):
|
||||
attrs = shorthand
|
||||
else:
|
||||
attrs = next(
|
||||
exp.match(shorthand).groupdict() # type: ignore[union-attr]
|
||||
for exp in regexps
|
||||
if exp.match(shorthand) is not None
|
||||
)
|
||||
|
||||
# Handle short form of the type expression
|
||||
if "type" in attrs:
|
||||
attrs["type"] = INV_TYPECODE_MAP.get(attrs["type"], attrs["type"])
|
||||
|
||||
# counts are quantitative by default
|
||||
if attrs == {"aggregate": "count"}:
|
||||
attrs["type"] = "quantitative"
|
||||
|
||||
# times are temporal by default
|
||||
if "timeUnit" in attrs and "type" not in attrs:
|
||||
attrs["type"] = "temporal"
|
||||
|
||||
# if data is specified and type is not, infer type from data
|
||||
if "type" not in attrs and is_data_type(data):
|
||||
unescaped_field = attrs["field"].replace("\\", "")
|
||||
data_nw = nw.from_native(data, eager_or_interchange_only=True)
|
||||
schema = data_nw.schema
|
||||
if unescaped_field in schema:
|
||||
column = data_nw[unescaped_field]
|
||||
if schema[unescaped_field] in {
|
||||
nw.Object,
|
||||
nw.Unknown,
|
||||
} and is_pandas_dataframe(data_nw.to_native()):
|
||||
attrs["type"] = infer_vegalite_type_for_pandas(column.to_native())
|
||||
else:
|
||||
attrs["type"] = infer_vegalite_type_for_narwhals(column)
|
||||
if isinstance(attrs["type"], tuple):
|
||||
attrs["sort"] = attrs["type"][1]
|
||||
attrs["type"] = attrs["type"][0]
|
||||
|
||||
# If an unescaped colon is still present, it's often due to an incorrect data type specification
|
||||
# but could also be due to using a column name with ":" in it.
|
||||
if (
|
||||
"field" in attrs
|
||||
and ":" in attrs["field"]
|
||||
and attrs["field"][attrs["field"].rfind(":") - 1] != "\\"
|
||||
):
|
||||
raise ValueError(
|
||||
'"{}" '.format(attrs["field"].split(":")[-1])
|
||||
+ "is not one of the valid encoding data types: {}.".format(
|
||||
", ".join(TYPECODE_MAP.values())
|
||||
)
|
||||
+ "\nFor more details, see https://altair-viz.github.io/user_guide/encodings/index.html#encoding-data-types. "
|
||||
+ "If you are trying to use a column name that contains a colon, "
|
||||
+ 'prefix it with a backslash; for example "column\\:name" instead of "column:name".'
|
||||
)
|
||||
return attrs
|
||||
|
||||
|
||||
def infer_vegalite_type_for_narwhals(
|
||||
column: nw.Series,
|
||||
) -> InferredVegaLiteType | tuple[InferredVegaLiteType, list]:
|
||||
dtype = column.dtype
|
||||
if (
|
||||
nw.is_ordered_categorical(column)
|
||||
and not (categories := column.cat.get_categories()).is_empty()
|
||||
):
|
||||
return "ordinal", categories.to_list()
|
||||
if dtype == nw.String or dtype == nw.Categorical or dtype == nw.Boolean: # noqa: PLR1714
|
||||
return "nominal"
|
||||
elif dtype.is_numeric():
|
||||
return "quantitative"
|
||||
elif dtype == nw.Datetime or dtype == nw.Date: # noqa: PLR1714
|
||||
# We use `== nw.Datetime` to check for any kind of Datetime, regardless of time
|
||||
# unit and time zone. Prefer this over `dtype in {nw.Datetime, nw.Date}`,
|
||||
# see https://narwhals-dev.github.io/narwhals/backcompat.
|
||||
return "temporal"
|
||||
else:
|
||||
msg = f"Unexpected DtypeKind: {dtype}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def use_signature(tp: Callable[P, Any], /):
|
||||
"""
|
||||
Use the signature and doc of ``tp`` for the decorated callable ``cb``.
|
||||
|
||||
- **Overload 1**: Decorating method
|
||||
- **Overload 2**: Decorating function
|
||||
|
||||
Returns
|
||||
-------
|
||||
**Adding the annotation breaks typing**:
|
||||
|
||||
Overload[Callable[[WrapsMethod[T, R]], WrappedMethod[T, P, R]], Callable[[WrapsFunc[R]], WrappedFunc[P, R]]]
|
||||
"""
|
||||
|
||||
@overload
|
||||
def decorate(cb: WrapsMethod[T, R], /) -> WrappedMethod[T, P, R]: ... # pyright: ignore[reportOverlappingOverload]
|
||||
|
||||
@overload
|
||||
def decorate(cb: WrapsFunc[R], /) -> WrappedFunc[P, R]: ... # pyright: ignore[reportOverlappingOverload]
|
||||
|
||||
def decorate(cb: WrapsFunc[R], /) -> WrappedMethod[T, P, R] | WrappedFunc[P, R]:
|
||||
"""
|
||||
Raises when no doc was found.
|
||||
|
||||
Notes
|
||||
-----
|
||||
- Reference to ``tp`` is stored in ``cb.__wrapped__``.
|
||||
- The doc for ``cb`` will have a ``.rst`` link added, referring to ``tp``.
|
||||
"""
|
||||
cb.__wrapped__ = getattr(tp, "__init__", tp) # type: ignore[attr-defined]
|
||||
|
||||
if doc_in := tp.__doc__:
|
||||
line_1 = f"{cb.__doc__ or f'Refer to :class:`{tp.__name__}`'}\n"
|
||||
cb.__doc__ = "".join((line_1, *doc_in.splitlines(keepends=True)[1:]))
|
||||
return cb
|
||||
else:
|
||||
msg = f"Found no doc for {tp!r}"
|
||||
raise AttributeError(msg)
|
||||
|
||||
return decorate
|
||||
|
||||
|
||||
@overload
|
||||
def update_nested(
|
||||
original: t.MutableMapping[Any, Any],
|
||||
update: t.Mapping[Any, Any],
|
||||
copy: Literal[False] = ...,
|
||||
) -> t.MutableMapping[Any, Any]: ...
|
||||
@overload
|
||||
def update_nested(
|
||||
original: t.Mapping[Any, Any],
|
||||
update: t.Mapping[Any, Any],
|
||||
copy: Literal[True],
|
||||
) -> t.MutableMapping[Any, Any]: ...
|
||||
def update_nested(
|
||||
original: Any,
|
||||
update: t.Mapping[Any, Any],
|
||||
copy: bool = False,
|
||||
) -> t.MutableMapping[Any, Any]:
|
||||
"""
|
||||
Update nested dictionaries.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
original : MutableMapping
|
||||
the original (nested) dictionary, which will be updated in-place
|
||||
update : Mapping
|
||||
the nested dictionary of updates
|
||||
copy : bool, default False
|
||||
if True, then copy the original dictionary rather than modifying it
|
||||
|
||||
Returns
|
||||
-------
|
||||
original : MutableMapping
|
||||
a reference to the (modified) original dict
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> original = {"x": {"b": 2, "c": 4}}
|
||||
>>> update = {"x": {"b": 5, "d": 6}, "y": 40}
|
||||
>>> update_nested(original, update) # doctest: +SKIP
|
||||
{'x': {'b': 5, 'c': 4, 'd': 6}, 'y': 40}
|
||||
>>> original # doctest: +SKIP
|
||||
{'x': {'b': 5, 'c': 4, 'd': 6}, 'y': 40}
|
||||
"""
|
||||
if copy:
|
||||
original = deepcopy(original)
|
||||
for key, val in update.items():
|
||||
if isinstance(val, Mapping):
|
||||
orig_val = original.get(key, {})
|
||||
if isinstance(orig_val, MutableMapping):
|
||||
original[key] = update_nested(orig_val, val)
|
||||
else:
|
||||
original[key] = val
|
||||
else:
|
||||
original[key] = val
|
||||
return original
|
||||
|
||||
|
||||
def display_traceback(in_ipython: bool = True):
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
if in_ipython:
|
||||
from IPython.core.getipython import get_ipython
|
||||
|
||||
ip = get_ipython()
|
||||
else:
|
||||
ip = None
|
||||
|
||||
if ip is not None:
|
||||
ip.showtraceback(exc_info)
|
||||
else:
|
||||
traceback.print_exception(*exc_info)
|
||||
|
||||
|
||||
_ChannelType = Literal["field", "datum", "value"]
|
||||
_CHANNEL_CACHE: _ChannelCache
|
||||
"""Singleton `_ChannelCache` instance.
|
||||
|
||||
Initialized on first use.
|
||||
"""
|
||||
|
||||
|
||||
class _ChannelCache:
|
||||
channel_to_name: dict[type[SchemaBase], str]
|
||||
name_to_channel: dict[str, dict[_ChannelType, type[SchemaBase]]]
|
||||
|
||||
@classmethod
|
||||
def from_cache(cls) -> _ChannelCache:
|
||||
global _CHANNEL_CACHE
|
||||
try:
|
||||
cached = _CHANNEL_CACHE
|
||||
except NameError:
|
||||
cached = cls.__new__(cls)
|
||||
cached.channel_to_name = _init_channel_to_name() # pyright: ignore[reportAttributeAccessIssue]
|
||||
cached.name_to_channel = _invert_group_channels(cached.channel_to_name)
|
||||
_CHANNEL_CACHE = cached
|
||||
return _CHANNEL_CACHE
|
||||
|
||||
def get_encoding(self, tp: type[Any], /) -> str:
|
||||
if encoding := self.channel_to_name.get(tp):
|
||||
return encoding
|
||||
msg = f"positional of type {type(tp).__name__!r}"
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def _wrap_in_channel(self, obj: Any, encoding: str, /):
|
||||
if isinstance(obj, SchemaBase):
|
||||
return obj
|
||||
elif isinstance(obj, str):
|
||||
obj = {"shorthand": obj}
|
||||
elif isinstance(obj, (list, tuple)):
|
||||
return [self._wrap_in_channel(el, encoding) for el in obj]
|
||||
elif isinstance(obj, SchemaLike):
|
||||
obj = obj.to_dict()
|
||||
if channel := self.name_to_channel.get(encoding):
|
||||
tp = channel["value" if "value" in obj else "field"]
|
||||
try:
|
||||
# Don't force validation here; some objects won't be valid until
|
||||
# they're created in the context of a chart.
|
||||
return tp.from_dict(obj, validate=False)
|
||||
except jsonschema.ValidationError:
|
||||
# our attempts at finding the correct class have failed
|
||||
return obj
|
||||
else:
|
||||
warnings.warn(f"Unrecognized encoding channel {encoding!r}", stacklevel=1)
|
||||
return obj
|
||||
|
||||
def infer_encoding_types(self, kwargs: dict[str, Any], /):
|
||||
return {
|
||||
encoding: self._wrap_in_channel(obj, encoding)
|
||||
for encoding, obj in kwargs.items()
|
||||
if obj is not Undefined
|
||||
}
|
||||
|
||||
|
||||
def _init_channel_to_name():
|
||||
"""
|
||||
Construct a dictionary of channel type to encoding name.
|
||||
|
||||
Note
|
||||
----
|
||||
The return type is not expressible using annotations, but is used
|
||||
internally by `mypy`/`pyright` and avoids the need for type ignores.
|
||||
|
||||
Returns
|
||||
-------
|
||||
mapping: dict[type[`<subclass of FieldChannelMixin and SchemaBase>`] | type[`<subclass of ValueChannelMixin and SchemaBase>`] | type[`<subclass of DatumChannelMixin and SchemaBase>`], str]
|
||||
"""
|
||||
from altair.vegalite.v5.schema import channels as ch
|
||||
|
||||
mixins = ch.FieldChannelMixin, ch.ValueChannelMixin, ch.DatumChannelMixin
|
||||
|
||||
return {
|
||||
c: c._encoding_name
|
||||
for c in ch.__dict__.values()
|
||||
if isinstance(c, type) and issubclass(c, mixins) and issubclass(c, SchemaBase)
|
||||
}
|
||||
|
||||
|
||||
def _invert_group_channels(
|
||||
m: dict[type[SchemaBase], str], /
|
||||
) -> dict[str, dict[_ChannelType, type[SchemaBase]]]:
|
||||
"""Grouped inverted index for `_ChannelCache.channel_to_name`."""
|
||||
|
||||
def _reduce(it: Iterator[tuple[type[Any], str]]) -> Any:
|
||||
"""
|
||||
Returns a 1-2 item dict, per channel.
|
||||
|
||||
Never includes `datum`, as it is never utilized in `wrap_in_channel`.
|
||||
"""
|
||||
item: dict[Any, type[SchemaBase]] = {}
|
||||
for tp, _ in it:
|
||||
name = tp.__name__
|
||||
if name.endswith("Datum"):
|
||||
continue
|
||||
elif name.endswith("Value"):
|
||||
sub_key = "value"
|
||||
else:
|
||||
sub_key = "field"
|
||||
item[sub_key] = tp
|
||||
return item
|
||||
|
||||
grouper = groupby(m.items(), itemgetter(1))
|
||||
return {k: _reduce(chans) for k, chans in grouper}
|
||||
|
||||
|
||||
def infer_encoding_types(args: tuple[Any, ...], kwargs: dict[str, Any]):
|
||||
"""
|
||||
Infer typed keyword arguments for args and kwargs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args : Sequence
|
||||
Sequence of function args
|
||||
kwargs : MutableMapping
|
||||
Dict of function kwargs
|
||||
|
||||
Returns
|
||||
-------
|
||||
kwargs : dict
|
||||
All args and kwargs in a single dict, with keys and types
|
||||
based on the channels mapping.
|
||||
"""
|
||||
cache = _ChannelCache.from_cache()
|
||||
# First use the mapping to convert args to kwargs based on their types.
|
||||
for arg in args:
|
||||
el = next(iter(arg), None) if isinstance(arg, (list, tuple)) else arg
|
||||
encoding = cache.get_encoding(type(el))
|
||||
if encoding not in kwargs:
|
||||
kwargs[encoding] = arg
|
||||
else:
|
||||
msg = f"encoding {encoding!r} specified twice."
|
||||
raise ValueError(msg)
|
||||
|
||||
return cache.infer_encoding_types(kwargs)
|
||||
442
myenv/lib/python3.11/site-packages/altair/utils/data.py
Normal file
442
myenv/lib/python3.11/site-packages/altair/utils/data.py
Normal file
@@ -0,0 +1,442 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import random
|
||||
import sys
|
||||
from collections.abc import MutableMapping, Sequence
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Literal,
|
||||
TypedDict,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
import narwhals.stable.v1 as nw
|
||||
from narwhals.stable.v1.dependencies import is_pandas_dataframe
|
||||
from narwhals.stable.v1.typing import IntoDataFrame
|
||||
|
||||
from ._importers import import_pyarrow_interchange
|
||||
from .core import (
|
||||
DataFrameLike,
|
||||
sanitize_geo_interface,
|
||||
sanitize_narwhals_dataframe,
|
||||
sanitize_pandas_dataframe,
|
||||
to_eager_narwhals_dataframe,
|
||||
)
|
||||
from .plugin_registry import PluginRegistry
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import Protocol, runtime_checkable
|
||||
else:
|
||||
from typing_extensions import Protocol, runtime_checkable
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import Concatenate, ParamSpec
|
||||
else:
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeIs
|
||||
else:
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
import pandas as pd
|
||||
import pyarrow as pa
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class SupportsGeoInterface(Protocol):
|
||||
__geo_interface__: MutableMapping
|
||||
|
||||
|
||||
DataType: TypeAlias = Union[
|
||||
dict[Any, Any], IntoDataFrame, SupportsGeoInterface, DataFrameLike
|
||||
]
|
||||
|
||||
TDataType = TypeVar("TDataType", bound=DataType)
|
||||
TIntoDataFrame = TypeVar("TIntoDataFrame", bound=IntoDataFrame)
|
||||
|
||||
VegaLiteDataDict: TypeAlias = dict[
|
||||
str, Union[str, dict[Any, Any], list[dict[Any, Any]]]
|
||||
]
|
||||
ToValuesReturnType: TypeAlias = dict[str, Union[dict[Any, Any], list[dict[Any, Any]]]]
|
||||
SampleReturnType = Union[IntoDataFrame, dict[str, Sequence], None]
|
||||
|
||||
|
||||
def is_data_type(obj: Any) -> TypeIs[DataType]:
|
||||
return isinstance(obj, (dict, SupportsGeoInterface)) or isinstance(
|
||||
nw.from_native(obj, eager_or_interchange_only=True, pass_through=True),
|
||||
nw.DataFrame,
|
||||
)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Data transformer registry
|
||||
#
|
||||
# A data transformer is a callable that takes a supported data type and returns
|
||||
# a transformed dictionary version of it which is compatible with the VegaLite schema.
|
||||
# The dict objects will be the Data portion of the VegaLite schema.
|
||||
#
|
||||
# Renderers only deal with the dict form of a
|
||||
# VegaLite spec, after the Data model has been put into a schema compliant
|
||||
# form.
|
||||
# ==============================================================================
|
||||
|
||||
P = ParamSpec("P")
|
||||
# NOTE: `Any` required due to the complexity of existing signatures imported in `altair.vegalite.v5.data.py`
|
||||
R = TypeVar("R", VegaLiteDataDict, Any)
|
||||
DataTransformerType = Callable[Concatenate[DataType, P], R]
|
||||
|
||||
|
||||
class DataTransformerRegistry(PluginRegistry[DataTransformerType, R]):
|
||||
_global_settings = {"consolidate_datasets": True}
|
||||
|
||||
@property
|
||||
def consolidate_datasets(self) -> bool:
|
||||
return self._global_settings["consolidate_datasets"]
|
||||
|
||||
@consolidate_datasets.setter
|
||||
def consolidate_datasets(self, value: bool) -> None:
|
||||
self._global_settings["consolidate_datasets"] = value
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
class MaxRowsError(Exception):
|
||||
"""Raised when a data model has too many rows."""
|
||||
|
||||
|
||||
@overload
|
||||
def limit_rows(data: None = ..., max_rows: int | None = ...) -> partial: ...
|
||||
@overload
|
||||
def limit_rows(data: DataType, max_rows: int | None = ...) -> DataType: ...
|
||||
def limit_rows(
|
||||
data: DataType | None = None, max_rows: int | None = 5000
|
||||
) -> partial | DataType:
|
||||
"""
|
||||
Raise MaxRowsError if the data model has more than max_rows.
|
||||
|
||||
If max_rows is None, then do not perform any check.
|
||||
"""
|
||||
if data is None:
|
||||
return partial(limit_rows, max_rows=max_rows)
|
||||
check_data_type(data)
|
||||
|
||||
def raise_max_rows_error():
|
||||
msg = (
|
||||
"The number of rows in your dataset is greater "
|
||||
f"than the maximum allowed ({max_rows}).\n\n"
|
||||
"Try enabling the VegaFusion data transformer which "
|
||||
"raises this limit by pre-evaluating data\n"
|
||||
"transformations in Python.\n"
|
||||
" >> import altair as alt\n"
|
||||
' >> alt.data_transformers.enable("vegafusion")\n\n'
|
||||
"Or, see https://altair-viz.github.io/user_guide/large_datasets.html "
|
||||
"for additional information\n"
|
||||
"on how to plot large datasets."
|
||||
)
|
||||
raise MaxRowsError(msg)
|
||||
|
||||
if isinstance(data, SupportsGeoInterface):
|
||||
if data.__geo_interface__["type"] == "FeatureCollection":
|
||||
values = data.__geo_interface__["features"]
|
||||
else:
|
||||
values = data.__geo_interface__
|
||||
elif isinstance(data, dict):
|
||||
if "values" in data:
|
||||
values = data["values"]
|
||||
else:
|
||||
return data
|
||||
else:
|
||||
data = to_eager_narwhals_dataframe(data)
|
||||
values = data
|
||||
|
||||
if max_rows is not None and len(values) > max_rows:
|
||||
raise_max_rows_error()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@overload
|
||||
def sample(
|
||||
data: None = ..., n: int | None = ..., frac: float | None = ...
|
||||
) -> partial: ...
|
||||
@overload
|
||||
def sample(
|
||||
data: TIntoDataFrame, n: int | None = ..., frac: float | None = ...
|
||||
) -> TIntoDataFrame: ...
|
||||
@overload
|
||||
def sample(
|
||||
data: DataType, n: int | None = ..., frac: float | None = ...
|
||||
) -> SampleReturnType: ...
|
||||
def sample(
|
||||
data: DataType | None = None,
|
||||
n: int | None = None,
|
||||
frac: float | None = None,
|
||||
) -> partial | SampleReturnType:
|
||||
"""Reduce the size of the data model by sampling without replacement."""
|
||||
if data is None:
|
||||
return partial(sample, n=n, frac=frac)
|
||||
check_data_type(data)
|
||||
if is_pandas_dataframe(data):
|
||||
return data.sample(n=n, frac=frac)
|
||||
elif isinstance(data, dict):
|
||||
if "values" in data:
|
||||
values = data["values"]
|
||||
if not n:
|
||||
if frac is None:
|
||||
msg = "frac cannot be None if n is None and data is a dictionary"
|
||||
raise ValueError(msg)
|
||||
n = int(frac * len(values))
|
||||
values = random.sample(values, n)
|
||||
return {"values": values}
|
||||
else:
|
||||
# Maybe this should raise an error or return something useful?
|
||||
return None
|
||||
data = nw.from_native(data, eager_only=True)
|
||||
if not n:
|
||||
if frac is None:
|
||||
msg = "frac cannot be None if n is None with this data input type"
|
||||
raise ValueError(msg)
|
||||
n = int(frac * len(data))
|
||||
indices = random.sample(range(len(data)), n)
|
||||
return data[indices].to_native()
|
||||
|
||||
|
||||
_FormatType = Literal["csv", "json"]
|
||||
|
||||
|
||||
class _FormatDict(TypedDict):
|
||||
type: _FormatType
|
||||
|
||||
|
||||
class _ToFormatReturnUrlDict(TypedDict):
|
||||
url: str
|
||||
format: _FormatDict
|
||||
|
||||
|
||||
@overload
|
||||
def to_json(
|
||||
data: None = ...,
|
||||
prefix: str = ...,
|
||||
extension: str = ...,
|
||||
filename: str = ...,
|
||||
urlpath: str = ...,
|
||||
) -> partial: ...
|
||||
|
||||
|
||||
@overload
|
||||
def to_json(
|
||||
data: DataType,
|
||||
prefix: str = ...,
|
||||
extension: str = ...,
|
||||
filename: str = ...,
|
||||
urlpath: str = ...,
|
||||
) -> _ToFormatReturnUrlDict: ...
|
||||
|
||||
|
||||
def to_json(
|
||||
data: DataType | None = None,
|
||||
prefix: str = "altair-data",
|
||||
extension: str = "json",
|
||||
filename: str = "{prefix}-{hash}.{extension}",
|
||||
urlpath: str = "",
|
||||
) -> partial | _ToFormatReturnUrlDict:
|
||||
"""Write the data model to a .json file and return a url based data model."""
|
||||
kwds = _to_text_kwds(prefix, extension, filename, urlpath)
|
||||
if data is None:
|
||||
return partial(to_json, **kwds)
|
||||
else:
|
||||
data_str = _data_to_json_string(data)
|
||||
return _to_text(data_str, **kwds, format=_FormatDict(type="json"))
|
||||
|
||||
|
||||
@overload
|
||||
def to_csv(
|
||||
data: None = ...,
|
||||
prefix: str = ...,
|
||||
extension: str = ...,
|
||||
filename: str = ...,
|
||||
urlpath: str = ...,
|
||||
) -> partial: ...
|
||||
|
||||
|
||||
@overload
|
||||
def to_csv(
|
||||
data: dict | pd.DataFrame | DataFrameLike,
|
||||
prefix: str = ...,
|
||||
extension: str = ...,
|
||||
filename: str = ...,
|
||||
urlpath: str = ...,
|
||||
) -> _ToFormatReturnUrlDict: ...
|
||||
|
||||
|
||||
def to_csv(
|
||||
data: dict | pd.DataFrame | DataFrameLike | None = None,
|
||||
prefix: str = "altair-data",
|
||||
extension: str = "csv",
|
||||
filename: str = "{prefix}-{hash}.{extension}",
|
||||
urlpath: str = "",
|
||||
) -> partial | _ToFormatReturnUrlDict:
|
||||
"""Write the data model to a .csv file and return a url based data model."""
|
||||
kwds = _to_text_kwds(prefix, extension, filename, urlpath)
|
||||
if data is None:
|
||||
return partial(to_csv, **kwds)
|
||||
else:
|
||||
data_str = _data_to_csv_string(data)
|
||||
return _to_text(data_str, **kwds, format=_FormatDict(type="csv"))
|
||||
|
||||
|
||||
def _to_text(
|
||||
data: str,
|
||||
prefix: str,
|
||||
extension: str,
|
||||
filename: str,
|
||||
urlpath: str,
|
||||
format: _FormatDict,
|
||||
) -> _ToFormatReturnUrlDict:
|
||||
data_hash = _compute_data_hash(data)
|
||||
filename = filename.format(prefix=prefix, hash=data_hash, extension=extension)
|
||||
Path(filename).write_text(data, encoding="utf-8")
|
||||
url = str(Path(urlpath, filename))
|
||||
return _ToFormatReturnUrlDict({"url": url, "format": format})
|
||||
|
||||
|
||||
def _to_text_kwds(prefix: str, extension: str, filename: str, urlpath: str, /) -> dict[str, str]: # fmt: skip
|
||||
return {"prefix": prefix, "extension": extension, "filename": filename, "urlpath": urlpath} # fmt: skip
|
||||
|
||||
|
||||
def to_values(data: DataType) -> ToValuesReturnType:
|
||||
"""Replace a DataFrame by a data model with values."""
|
||||
check_data_type(data)
|
||||
# `pass_through=True` passes `data` through as-is if it is not a Narwhals object.
|
||||
data_native = nw.to_native(data, pass_through=True)
|
||||
if isinstance(data_native, SupportsGeoInterface):
|
||||
return {"values": _from_geo_interface(data_native)}
|
||||
elif is_pandas_dataframe(data_native):
|
||||
data_native = sanitize_pandas_dataframe(data_native)
|
||||
return {"values": data_native.to_dict(orient="records")}
|
||||
elif isinstance(data_native, dict):
|
||||
if "values" not in data_native:
|
||||
msg = "values expected in data dict, but not present."
|
||||
raise KeyError(msg)
|
||||
return data_native
|
||||
elif isinstance(data, nw.DataFrame):
|
||||
data = sanitize_narwhals_dataframe(data)
|
||||
return {"values": data.rows(named=True)}
|
||||
else:
|
||||
# Should never reach this state as tested by check_data_type
|
||||
msg = f"Unrecognized data type: {type(data)}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def check_data_type(data: DataType) -> None:
|
||||
if not is_data_type(data):
|
||||
msg = f"Expected dict, DataFrame or a __geo_interface__ attribute, got: {type(data)}"
|
||||
raise TypeError(msg)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# Private utilities
|
||||
# ==============================================================================
|
||||
def _compute_data_hash(data_str: str) -> str:
|
||||
return hashlib.sha256(data_str.encode()).hexdigest()[:32]
|
||||
|
||||
|
||||
def _from_geo_interface(data: SupportsGeoInterface | Any) -> dict[str, Any]:
|
||||
"""
|
||||
Santize a ``__geo_interface__`` w/ pre-santize step for ``pandas`` if needed.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Split out to resolve typing issues related to:
|
||||
- Intersection types
|
||||
- ``typing.TypeGuard``
|
||||
- ``pd.DataFrame.__getattr__``
|
||||
"""
|
||||
if is_pandas_dataframe(data):
|
||||
data = sanitize_pandas_dataframe(data)
|
||||
return sanitize_geo_interface(data.__geo_interface__)
|
||||
|
||||
|
||||
def _data_to_json_string(data: DataType) -> str:
|
||||
"""Return a JSON string representation of the input data."""
|
||||
check_data_type(data)
|
||||
if isinstance(data, SupportsGeoInterface):
|
||||
return json.dumps(_from_geo_interface(data))
|
||||
elif is_pandas_dataframe(data):
|
||||
data = sanitize_pandas_dataframe(data)
|
||||
return data.to_json(orient="records", double_precision=15)
|
||||
elif isinstance(data, dict):
|
||||
if "values" not in data:
|
||||
msg = "values expected in data dict, but not present."
|
||||
raise KeyError(msg)
|
||||
return json.dumps(data["values"], sort_keys=True)
|
||||
try:
|
||||
data_nw = nw.from_native(data, eager_only=True)
|
||||
except TypeError as exc:
|
||||
msg = "to_json only works with data expressed as a DataFrame or as a dict"
|
||||
raise NotImplementedError(msg) from exc
|
||||
data_nw = sanitize_narwhals_dataframe(data_nw)
|
||||
return json.dumps(data_nw.rows(named=True))
|
||||
|
||||
|
||||
def _data_to_csv_string(data: DataType) -> str:
|
||||
"""Return a CSV string representation of the input data."""
|
||||
check_data_type(data)
|
||||
if isinstance(data, SupportsGeoInterface):
|
||||
msg = (
|
||||
f"to_csv does not yet work with data that "
|
||||
f"is of type {type(SupportsGeoInterface).__name__!r}.\n"
|
||||
f"See https://github.com/vega/altair/issues/3441"
|
||||
)
|
||||
raise NotImplementedError(msg)
|
||||
elif is_pandas_dataframe(data):
|
||||
data = sanitize_pandas_dataframe(data)
|
||||
return data.to_csv(index=False)
|
||||
elif isinstance(data, dict):
|
||||
if "values" not in data:
|
||||
msg = "values expected in data dict, but not present"
|
||||
raise KeyError(msg)
|
||||
try:
|
||||
import pandas as pd
|
||||
except ImportError as exc:
|
||||
msg = "pandas is required to convert a dict to a CSV string"
|
||||
raise ImportError(msg) from exc
|
||||
return pd.DataFrame.from_dict(data["values"]).to_csv(index=False)
|
||||
try:
|
||||
data_nw = nw.from_native(data, eager_only=True)
|
||||
except TypeError as exc:
|
||||
msg = "to_csv only works with data expressed as a DataFrame or as a dict"
|
||||
raise NotImplementedError(msg) from exc
|
||||
return data_nw.write_csv()
|
||||
|
||||
|
||||
def arrow_table_from_dfi_dataframe(dfi_df: DataFrameLike) -> pa.Table:
|
||||
"""Convert a DataFrame Interchange Protocol compatible object to an Arrow Table."""
|
||||
import pyarrow as pa
|
||||
|
||||
# First check if the dataframe object has a method to convert to arrow.
|
||||
# Give this preference over the pyarrow from_dataframe function since the object
|
||||
# has more control over the conversion, and may have broader compatibility.
|
||||
# This is the case for Polars, which supports Date32 columns in direct conversion
|
||||
# while pyarrow does not yet support this type in from_dataframe
|
||||
for convert_method_name in ("arrow", "to_arrow", "to_arrow_table", "to_pyarrow"):
|
||||
convert_method = getattr(dfi_df, convert_method_name, None)
|
||||
if callable(convert_method):
|
||||
result = convert_method()
|
||||
if isinstance(result, pa.Table):
|
||||
return result
|
||||
|
||||
pi = import_pyarrow_interchange()
|
||||
return pi.from_dataframe(dfi_df)
|
||||
196
myenv/lib/python3.11/site-packages/altair/utils/deprecation.py
Normal file
196
myenv/lib/python3.11/site-packages/altair/utils/deprecation.py
Normal file
@@ -0,0 +1,196 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from warnings import deprecated as _deprecated
|
||||
else:
|
||||
from typing_extensions import deprecated as _deprecated
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import LiteralString
|
||||
else:
|
||||
from typing_extensions import LiteralString
|
||||
|
||||
__all__ = [
|
||||
"AltairDeprecationWarning",
|
||||
"deprecated",
|
||||
"deprecated_static_only",
|
||||
"deprecated_warn",
|
||||
]
|
||||
|
||||
|
||||
class AltairDeprecationWarning(DeprecationWarning): ...
|
||||
|
||||
|
||||
def _format_message(
|
||||
version: LiteralString,
|
||||
alternative: LiteralString | None,
|
||||
message: LiteralString | None,
|
||||
/,
|
||||
) -> LiteralString:
|
||||
output = f"\nDeprecated since `altair={version}`."
|
||||
if alternative:
|
||||
output = f"{output} Use {alternative} instead."
|
||||
return f"{output}\n{message}" if message else output
|
||||
|
||||
|
||||
# NOTE: Annotating the return type breaks `pyright` detecting [reportDeprecated]
|
||||
# NOTE: `LiteralString` requirement is introduced by stubs
|
||||
def deprecated(
|
||||
*,
|
||||
version: LiteralString,
|
||||
alternative: LiteralString | None = None,
|
||||
message: LiteralString | None = None,
|
||||
category: type[AltairDeprecationWarning] | None = AltairDeprecationWarning,
|
||||
stacklevel: int = 1,
|
||||
): # te.deprecated
|
||||
"""
|
||||
Indicate that a class, function or overload is deprecated.
|
||||
|
||||
When this decorator is applied to an object, the type checker
|
||||
will generate a diagnostic on usage of the deprecated object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
version
|
||||
``altair`` version the deprecation first appeared.
|
||||
alternative
|
||||
Suggested replacement class/method/function.
|
||||
message
|
||||
Additional message appended to ``version``, ``alternative``.
|
||||
category
|
||||
If the *category* is ``None``, no warning is emitted at runtime.
|
||||
stacklevel
|
||||
The *stacklevel* determines where the
|
||||
warning is emitted. If it is ``1`` (the default), the warning
|
||||
is emitted at the direct caller of the deprecated object; if it
|
||||
is higher, it is emitted further up the stack.
|
||||
Static type checker behavior is not affected by the *category*
|
||||
and *stacklevel* arguments.
|
||||
|
||||
References
|
||||
----------
|
||||
[PEP 702](https://peps.python.org/pep-0702/)
|
||||
"""
|
||||
msg = _format_message(version, alternative, message)
|
||||
return _deprecated(msg, category=category, stacklevel=stacklevel)
|
||||
|
||||
|
||||
def deprecated_warn(
|
||||
message: LiteralString,
|
||||
*,
|
||||
version: LiteralString,
|
||||
alternative: LiteralString | None = None,
|
||||
category: type[AltairDeprecationWarning] = AltairDeprecationWarning,
|
||||
stacklevel: int = 2,
|
||||
action: Literal["once"] | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Indicate that the current code path is deprecated.
|
||||
|
||||
This should be used for non-trivial cases *only*. ``@deprecated`` should
|
||||
always be preferred as it is recognized by static type checkers.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
message
|
||||
Explanation of the deprecated behaviour.
|
||||
|
||||
.. note::
|
||||
Unlike ``@deprecated``, this is *not* optional.
|
||||
|
||||
version
|
||||
``altair`` version the deprecation first appeared.
|
||||
alternative
|
||||
Suggested replacement argument/method/function.
|
||||
category
|
||||
The runtime warning type emitted.
|
||||
stacklevel
|
||||
How far up the call stack to make this warning appear.
|
||||
A value of ``2`` attributes the warning to the caller
|
||||
of the code calling ``deprecated_warn()``.
|
||||
|
||||
References
|
||||
----------
|
||||
[warnings.warn](https://docs.python.org/3/library/warnings.html#warnings.warn)
|
||||
"""
|
||||
msg = _format_message(version, alternative, message)
|
||||
if action is None:
|
||||
warnings.warn(msg, category=category, stacklevel=stacklevel)
|
||||
elif action == "once":
|
||||
_warn_once(msg, category=category, stacklevel=stacklevel)
|
||||
else:
|
||||
raise NotImplementedError(action)
|
||||
|
||||
|
||||
deprecated_static_only = _deprecated
|
||||
"""
|
||||
Using this decorator **exactly as described**, ensures ``message`` is displayed to a static type checker.
|
||||
|
||||
**BE CAREFUL USING THIS**.
|
||||
|
||||
See screenshots in `comment`_ for motivation.
|
||||
|
||||
Every use should look like::
|
||||
|
||||
@deprecated_static_only(
|
||||
"Deprecated since `altair=5.5.0`. Use altair.other instead.",
|
||||
category=None,
|
||||
)
|
||||
def old_function(*args): ...
|
||||
|
||||
If a runtime warning is desired, use `@alt.utils.deprecated` instead.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
message : LiteralString
|
||||
- **Not** a variable
|
||||
- **Not** use placeholders
|
||||
- **Not** use concatenation
|
||||
- **Do not use anything that could be considered dynamic**
|
||||
|
||||
category : None
|
||||
You **need** to explicitly pass ``None``
|
||||
|
||||
.. _comment:
|
||||
https://github.com/vega/altair/pull/3618#issuecomment-2423991968
|
||||
---
|
||||
"""
|
||||
|
||||
|
||||
class _WarningsMonitor:
|
||||
def __init__(self) -> None:
|
||||
self._warned: dict[LiteralString, Literal[True]] = {}
|
||||
self._lock = threading.Lock()
|
||||
|
||||
def __contains__(self, key: LiteralString, /) -> bool:
|
||||
with self._lock:
|
||||
return key in self._warned
|
||||
|
||||
def hit(self, key: LiteralString, /) -> None:
|
||||
with self._lock:
|
||||
self._warned[key] = True
|
||||
|
||||
def clear(self) -> None:
|
||||
with self._lock:
|
||||
self._warned.clear()
|
||||
|
||||
|
||||
_warnings_monitor = _WarningsMonitor()
|
||||
|
||||
|
||||
def _warn_once(
|
||||
msg: LiteralString, /, *, category: type[AltairDeprecationWarning], stacklevel: int
|
||||
) -> None:
|
||||
global _warnings_monitor
|
||||
if msg in _warnings_monitor:
|
||||
return
|
||||
else:
|
||||
_warnings_monitor.hit(msg)
|
||||
warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||
232
myenv/lib/python3.11/site-packages/altair/utils/display.py
Normal file
232
myenv/lib/python3.11/site-packages/altair/utils/display.py
Normal file
@@ -0,0 +1,232 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pkgutil
|
||||
import textwrap
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING, Any, Callable, Union
|
||||
|
||||
from ._vegafusion_data import compile_with_vegafusion, using_vegafusion
|
||||
from .mimebundle import spec_to_mimebundle
|
||||
from .plugin_registry import PluginEnabler, PluginRegistry
|
||||
from .schemapi import validate_jsonschema
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# ==============================================================================
|
||||
# Renderer registry
|
||||
# ==============================================================================
|
||||
# MimeBundleType needs to be the same as what are acceptable return values
|
||||
# for _repr_mimebundle_,
|
||||
# see https://ipython.readthedocs.io/en/stable/config/integrating.html#MyObject._repr_mimebundle_
|
||||
MimeBundleDataType: TypeAlias = dict[str, Any]
|
||||
MimeBundleMetaDataType: TypeAlias = dict[str, Any]
|
||||
MimeBundleType: TypeAlias = Union[
|
||||
MimeBundleDataType, tuple[MimeBundleDataType, MimeBundleMetaDataType]
|
||||
]
|
||||
RendererType: TypeAlias = Callable[..., MimeBundleType]
|
||||
# Subtype of MimeBundleType as more specific in the values of the dictionaries
|
||||
|
||||
DefaultRendererReturnType: TypeAlias = tuple[
|
||||
dict[str, Union[str, dict[str, Any]]], dict[str, dict[str, Any]]
|
||||
]
|
||||
|
||||
|
||||
class RendererRegistry(PluginRegistry[RendererType, MimeBundleType]):
|
||||
entrypoint_err_messages = {
|
||||
"notebook": textwrap.dedent(
|
||||
"""
|
||||
To use the 'notebook' renderer, you must install the vega package
|
||||
and the associated Jupyter extension.
|
||||
See https://altair-viz.github.io/getting_started/installation.html
|
||||
for more information.
|
||||
"""
|
||||
),
|
||||
}
|
||||
|
||||
def set_embed_options(
|
||||
self,
|
||||
defaultStyle: bool | str | None = None,
|
||||
renderer: str | None = None,
|
||||
width: int | None = None,
|
||||
height: int | None = None,
|
||||
padding: int | None = None,
|
||||
scaleFactor: float | None = None,
|
||||
actions: bool | dict[str, bool] | None = None,
|
||||
format_locale: str | dict | None = None,
|
||||
time_format_locale: str | dict | None = None,
|
||||
**kwargs,
|
||||
) -> PluginEnabler:
|
||||
"""
|
||||
Set options for embeddings of Vega & Vega-Lite charts.
|
||||
|
||||
Options are fully documented at https://github.com/vega/vega-embed.
|
||||
Similar to the `enable()` method, this can be used as either
|
||||
a persistent global switch, or as a temporary local setting using
|
||||
a context manager (i.e. a `with` statement).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
defaultStyle : bool or string
|
||||
Specify a default stylesheet for embed actions.
|
||||
renderer : string
|
||||
The renderer to use for the view. One of "canvas" (default) or "svg"
|
||||
width : integer
|
||||
The view width in pixels
|
||||
height : integer
|
||||
The view height in pixels
|
||||
padding : integer
|
||||
The view padding in pixels
|
||||
scaleFactor : number
|
||||
The number by which to multiply the width and height (default 1)
|
||||
of an exported PNG or SVG image.
|
||||
actions : bool or dict
|
||||
Determines if action links ("Export as PNG/SVG", "View Source",
|
||||
"View Vega" (only for Vega-Lite), "Open in Vega Editor") are
|
||||
included with the embedded view. If the value is true, all action
|
||||
links will be shown and none if the value is false. This property
|
||||
can take a key-value mapping object that maps keys (export, source,
|
||||
compiled, editor) to boolean values for determining if
|
||||
each action link should be shown.
|
||||
format_locale : str or dict
|
||||
d3-format locale name or dictionary. Defaults to "en-US" for United States English.
|
||||
See https://github.com/d3/d3-format/tree/main/locale for available names and example
|
||||
definitions.
|
||||
time_format_locale : str or dict
|
||||
d3-time-format locale name or dictionary. Defaults to "en-US" for United States English.
|
||||
See https://github.com/d3/d3-time-format/tree/main/locale for available names and example
|
||||
definitions.
|
||||
**kwargs :
|
||||
Additional options are passed directly to embed options.
|
||||
"""
|
||||
options: dict[str, bool | str | float | dict[str, bool] | None] = {
|
||||
"defaultStyle": defaultStyle,
|
||||
"renderer": renderer,
|
||||
"width": width,
|
||||
"height": height,
|
||||
"padding": padding,
|
||||
"scaleFactor": scaleFactor,
|
||||
"actions": actions,
|
||||
"formatLocale": format_locale,
|
||||
"timeFormatLocale": time_format_locale,
|
||||
}
|
||||
kwargs.update({key: val for key, val in options.items() if val is not None})
|
||||
return self.enable(None, embed_options=kwargs)
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# VegaLite v1/v2 renderer logic
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
class Displayable:
|
||||
"""
|
||||
A base display class for VegaLite v1/v2.
|
||||
|
||||
This class takes a VegaLite v1/v2 spec and does the following:
|
||||
|
||||
1. Optionally validates the spec against a schema.
|
||||
2. Uses the RendererPlugin to grab a renderer and call it when the
|
||||
IPython/Jupyter display method (_repr_mimebundle_) is called.
|
||||
|
||||
The spec passed to this class must be fully schema compliant and already
|
||||
have the data portion of the spec fully processed and ready to serialize.
|
||||
In practice, this means, the data portion of the spec should have been passed
|
||||
through appropriate data model transformers.
|
||||
"""
|
||||
|
||||
renderers: RendererRegistry | None = None
|
||||
schema_path = ("altair", "")
|
||||
|
||||
def __init__(self, spec: dict[str, Any], validate: bool = False) -> None:
|
||||
self.spec = spec
|
||||
self.validate = validate
|
||||
self._validate()
|
||||
|
||||
def _validate(self) -> None:
|
||||
"""Validate the spec against the schema."""
|
||||
data = pkgutil.get_data(*self.schema_path)
|
||||
assert data is not None
|
||||
schema_dict: dict[str, Any] = json.loads(data.decode("utf-8"))
|
||||
validate_jsonschema(
|
||||
self.spec,
|
||||
schema_dict,
|
||||
)
|
||||
|
||||
def _repr_mimebundle_(
|
||||
self, include: Any = None, exclude: Any = None
|
||||
) -> MimeBundleType:
|
||||
"""Return a MIME bundle for display in Jupyter frontends."""
|
||||
if self.renderers is not None:
|
||||
renderer_func = self.renderers.get()
|
||||
assert renderer_func is not None
|
||||
return renderer_func(self.spec)
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
def default_renderer_base(
|
||||
spec: dict[str, Any], mime_type: str, str_repr: str, **options
|
||||
) -> DefaultRendererReturnType:
|
||||
"""
|
||||
A default renderer for Vega or VegaLite that works for modern frontends.
|
||||
|
||||
This renderer works with modern frontends (JupyterLab, nteract) that know
|
||||
how to render the custom VegaLite MIME type listed above.
|
||||
"""
|
||||
# Local import to avoid circular ImportError
|
||||
from altair.vegalite.v5.display import VEGA_MIME_TYPE, VEGALITE_MIME_TYPE
|
||||
|
||||
assert isinstance(spec, dict)
|
||||
bundle: dict[str, str | dict] = {}
|
||||
metadata: dict[str, dict[str, Any]] = {}
|
||||
|
||||
if using_vegafusion():
|
||||
spec = compile_with_vegafusion(spec)
|
||||
|
||||
# Swap mimetype from Vega-Lite to Vega.
|
||||
# If mimetype was JSON, leave it alone
|
||||
if mime_type == VEGALITE_MIME_TYPE:
|
||||
mime_type = VEGA_MIME_TYPE
|
||||
|
||||
bundle[mime_type] = spec
|
||||
bundle["text/plain"] = str_repr
|
||||
if options:
|
||||
metadata[mime_type] = options
|
||||
return bundle, metadata
|
||||
|
||||
|
||||
def json_renderer_base(
|
||||
spec: dict[str, Any], str_repr: str, **options
|
||||
) -> DefaultRendererReturnType:
|
||||
"""
|
||||
A renderer that returns a MIME type of application/json.
|
||||
|
||||
In JupyterLab/nteract this is rendered as a nice JSON tree.
|
||||
"""
|
||||
return default_renderer_base(
|
||||
spec, mime_type="application/json", str_repr=str_repr, **options
|
||||
)
|
||||
|
||||
|
||||
class HTMLRenderer:
|
||||
"""Object to render charts as HTML, with a unique output div each time."""
|
||||
|
||||
def __init__(self, output_div: str = "altair-viz-{}", **kwargs) -> None:
|
||||
self._output_div = output_div
|
||||
self.kwargs = kwargs
|
||||
|
||||
@property
|
||||
def output_div(self) -> str:
|
||||
return self._output_div.format(uuid.uuid4().hex)
|
||||
|
||||
def __call__(self, spec: dict[str, Any], **metadata) -> dict[str, str]:
|
||||
kwargs = self.kwargs.copy()
|
||||
kwargs.update(**metadata, output_div=self.output_div)
|
||||
return spec_to_mimebundle(spec, format="html", **kwargs)
|
||||
98
myenv/lib/python3.11/site-packages/altair/utils/execeval.py
Normal file
98
myenv/lib/python3.11/site-packages/altair/utils/execeval.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Any, Callable, Literal, overload
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from os import PathLike
|
||||
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import Self
|
||||
else:
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class _CatchDisplay:
|
||||
"""Class to temporarily catch sys.displayhook."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.output: Any | None = None
|
||||
|
||||
def __enter__(self) -> Self:
|
||||
self.old_hook: Callable[[object], Any] = sys.displayhook
|
||||
sys.displayhook = self
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback) -> Literal[False]:
|
||||
sys.displayhook = self.old_hook
|
||||
# Returning False will cause exceptions to propagate
|
||||
return False
|
||||
|
||||
def __call__(self, output: Any) -> None:
|
||||
self.output = output
|
||||
|
||||
|
||||
@overload
|
||||
def eval_block(
|
||||
code: str | Any,
|
||||
namespace: dict[str, Any] | None = ...,
|
||||
filename: str | ReadableBuffer | PathLike[Any] = ...,
|
||||
*,
|
||||
strict: Literal[False] = ...,
|
||||
) -> Any | None: ...
|
||||
@overload
|
||||
def eval_block(
|
||||
code: str | Any,
|
||||
namespace: dict[str, Any] | None = ...,
|
||||
filename: str | ReadableBuffer | PathLike[Any] = ...,
|
||||
*,
|
||||
strict: Literal[True] = ...,
|
||||
) -> Any: ...
|
||||
def eval_block(
|
||||
code: str | Any,
|
||||
namespace: dict[str, Any] | None = None,
|
||||
filename: str | ReadableBuffer | PathLike[Any] = "<string>",
|
||||
*,
|
||||
strict: bool = False,
|
||||
) -> Any | None:
|
||||
"""
|
||||
Execute a multi-line block of code in the given namespace.
|
||||
|
||||
If the final statement in the code is an expression, return
|
||||
the result of the expression.
|
||||
|
||||
If ``strict``, raise a ``TypeError`` when the return value would be ``None``.
|
||||
"""
|
||||
tree = ast.parse(code, filename="<ast>", mode="exec")
|
||||
if namespace is None:
|
||||
namespace = {}
|
||||
catch_display = _CatchDisplay()
|
||||
|
||||
if isinstance(tree.body[-1], ast.Expr):
|
||||
to_exec, to_eval = tree.body[:-1], tree.body[-1:]
|
||||
else:
|
||||
to_exec, to_eval = tree.body, []
|
||||
|
||||
for node in to_exec:
|
||||
compiled = compile(ast.Module([node], []), filename=filename, mode="exec")
|
||||
exec(compiled, namespace)
|
||||
|
||||
with catch_display:
|
||||
for node in to_eval:
|
||||
compiled = compile(
|
||||
ast.Interactive([node]), filename=filename, mode="single"
|
||||
)
|
||||
exec(compiled, namespace)
|
||||
|
||||
if strict:
|
||||
output = catch_display.output
|
||||
if output is None:
|
||||
msg = f"Expected a non-None value but got {output!r}"
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
return output
|
||||
else:
|
||||
return catch_display.output
|
||||
411
myenv/lib/python3.11/site-packages/altair/utils/html.py
Normal file
411
myenv/lib/python3.11/site-packages/altair/utils/html.py
Normal file
@@ -0,0 +1,411 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any, Literal
|
||||
|
||||
import jinja2
|
||||
|
||||
from altair.utils._importers import import_vl_convert, vl_version_for_vl_convert
|
||||
|
||||
TemplateName = Literal["standard", "universal", "inline", "olli"]
|
||||
RenderMode = Literal["vega", "vega-lite"]
|
||||
|
||||
HTML_TEMPLATE = jinja2.Template(
|
||||
"""
|
||||
{%- if fullhtml -%}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{%- endif %}
|
||||
<style>
|
||||
#{{ output_div }}.vega-embed {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#{{ output_div }}.vega-embed details,
|
||||
#{{ output_div }}.vega-embed details summary {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
{%- if not requirejs %}
|
||||
<script type="text/javascript" src="{{ base_url }}/vega@{{ vega_version }}"></script>
|
||||
{%- if mode == 'vega-lite' %}
|
||||
<script type="text/javascript" src="{{ base_url }}/vega-lite@{{ vegalite_version }}"></script>
|
||||
{%- endif %}
|
||||
<script type="text/javascript" src="{{ base_url }}/vega-embed@{{ vegaembed_version }}"></script>
|
||||
{%- endif %}
|
||||
{%- if fullhtml %}
|
||||
{%- if requirejs %}
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
|
||||
<script>
|
||||
requirejs.config({
|
||||
"paths": {
|
||||
"vega": "{{ base_url }}/vega@{{ vega_version }}?noext",
|
||||
"vega-lib": "{{ base_url }}/vega-lib?noext",
|
||||
"vega-lite": "{{ base_url }}/vega-lite@{{ vegalite_version }}?noext",
|
||||
"vega-embed": "{{ base_url }}/vega-embed@{{ vegaembed_version }}?noext",
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{%- endif %}
|
||||
</head>
|
||||
<body>
|
||||
{%- endif %}
|
||||
<div id="{{ output_div }}"></div>
|
||||
<script>
|
||||
{%- if requirejs and not fullhtml %}
|
||||
requirejs.config({
|
||||
"paths": {
|
||||
"vega": "{{ base_url }}/vega@{{ vega_version }}?noext",
|
||||
"vega-lib": "{{ base_url }}/vega-lib?noext",
|
||||
"vega-lite": "{{ base_url }}/vega-lite@{{ vegalite_version }}?noext",
|
||||
"vega-embed": "{{ base_url }}/vega-embed@{{ vegaembed_version }}?noext",
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
{% if requirejs -%}
|
||||
require(['vega-embed'],
|
||||
{%- else -%}
|
||||
(
|
||||
{%- endif -%}
|
||||
function(vegaEmbed) {
|
||||
var spec = {{ spec }};
|
||||
var embedOpt = {{ embed_options }};
|
||||
|
||||
function showError(el, error){
|
||||
el.innerHTML = ('<div style="color:red;">'
|
||||
+ '<p>JavaScript Error: ' + error.message + '</p>'
|
||||
+ "<p>This usually means there's a typo in your chart specification. "
|
||||
+ "See the javascript console for the full traceback.</p>"
|
||||
+ '</div>');
|
||||
throw error;
|
||||
}
|
||||
const el = document.getElementById('{{ output_div }}');
|
||||
vegaEmbed("#{{ output_div }}", spec, embedOpt)
|
||||
.catch(error => showError(el, error));
|
||||
}){% if not requirejs %}(vegaEmbed){% endif %};
|
||||
|
||||
</script>
|
||||
{%- if fullhtml %}
|
||||
</body>
|
||||
</html>
|
||||
{%- endif %}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
HTML_TEMPLATE_UNIVERSAL = jinja2.Template(
|
||||
"""
|
||||
<style>
|
||||
#{{ output_div }}.vega-embed {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#{{ output_div }}.vega-embed details,
|
||||
#{{ output_div }}.vega-embed details summary {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<div id="{{ output_div }}"></div>
|
||||
<script type="text/javascript">
|
||||
var VEGA_DEBUG = (typeof VEGA_DEBUG == "undefined") ? {} : VEGA_DEBUG;
|
||||
(function(spec, embedOpt){
|
||||
let outputDiv = document.currentScript.previousElementSibling;
|
||||
if (outputDiv.id !== "{{ output_div }}") {
|
||||
outputDiv = document.getElementById("{{ output_div }}");
|
||||
}
|
||||
|
||||
const paths = {
|
||||
"vega": "{{ base_url }}/vega@{{ vega_version }}?noext",
|
||||
"vega-lib": "{{ base_url }}/vega-lib?noext",
|
||||
"vega-lite": "{{ base_url }}/vega-lite@{{ vegalite_version }}?noext",
|
||||
"vega-embed": "{{ base_url }}/vega-embed@{{ vegaembed_version }}?noext",
|
||||
};
|
||||
|
||||
function maybeLoadScript(lib, version) {
|
||||
var key = `${lib.replace("-", "")}_version`;
|
||||
return (VEGA_DEBUG[key] == version) ?
|
||||
Promise.resolve(paths[lib]) :
|
||||
new Promise(function(resolve, reject) {
|
||||
var s = document.createElement('script');
|
||||
document.getElementsByTagName("head")[0].appendChild(s);
|
||||
s.async = true;
|
||||
s.onload = () => {
|
||||
VEGA_DEBUG[key] = version;
|
||||
return resolve(paths[lib]);
|
||||
};
|
||||
s.onerror = () => reject(`Error loading script: ${paths[lib]}`);
|
||||
s.src = paths[lib];
|
||||
});
|
||||
}
|
||||
|
||||
function showError(err) {
|
||||
outputDiv.innerHTML = `<div class="error" style="color:red;">${err}</div>`;
|
||||
throw err;
|
||||
}
|
||||
|
||||
function displayChart(vegaEmbed) {
|
||||
vegaEmbed(outputDiv, spec, embedOpt)
|
||||
.catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));
|
||||
}
|
||||
|
||||
if(typeof define === "function" && define.amd) {
|
||||
requirejs.config({paths});
|
||||
let deps = ["vega-embed"];
|
||||
require(deps, displayChart, err => showError(`Error loading script: ${err.message}`));
|
||||
} else {
|
||||
maybeLoadScript("vega", "{{vega_version}}")
|
||||
.then(() => maybeLoadScript("vega-lite", "{{vegalite_version}}"))
|
||||
.then(() => maybeLoadScript("vega-embed", "{{vegaembed_version}}"))
|
||||
.catch(showError)
|
||||
.then(() => displayChart(vegaEmbed));
|
||||
}
|
||||
})({{ spec }}, {{ embed_options }});
|
||||
</script>
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
# This is like the HTML_TEMPLATE template, but includes vega javascript inline
|
||||
# so that the resulting file is not dependent on external resources. This was
|
||||
# ported over from altair_saver.
|
||||
#
|
||||
# implies requirejs=False and full_html=True
|
||||
INLINE_HTML_TEMPLATE = jinja2.Template(
|
||||
"""\
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
#{{ output_div }}.vega-embed {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#{{ output_div }}.vega-embed details,
|
||||
#{{ output_div }}.vega-embed details summary {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
// vega-embed.js bundle with Vega-Lite version v{{ vegalite_version }}
|
||||
{{ vegaembed_script }}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="vega-visualization" id="{{ output_div }}"></div>
|
||||
<script type="text/javascript">
|
||||
const spec = {{ spec }};
|
||||
const embedOpt = {{ embed_options }};
|
||||
vegaEmbed('#{{ output_div }}', spec, embedOpt).catch(console.error);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
HTML_TEMPLATE_OLLI = jinja2.Template(
|
||||
"""
|
||||
<style>
|
||||
#{{ output_div }}.vega-embed {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#{{ output_div }}.vega-embed details,
|
||||
#{{ output_div }}.vega-embed details summary {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<div id="{{ output_div }}"></div>
|
||||
<script type="text/javascript">
|
||||
var VEGA_DEBUG = (typeof VEGA_DEBUG == "undefined") ? {} : VEGA_DEBUG;
|
||||
(function(spec, embedOpt){
|
||||
let outputDiv = document.currentScript.previousElementSibling;
|
||||
if (outputDiv.id !== "{{ output_div }}") {
|
||||
outputDiv = document.getElementById("{{ output_div }}");
|
||||
}
|
||||
const olliDiv = document.createElement("div");
|
||||
const vegaDiv = document.createElement("div");
|
||||
outputDiv.appendChild(vegaDiv);
|
||||
outputDiv.appendChild(olliDiv);
|
||||
outputDiv = vegaDiv;
|
||||
|
||||
const paths = {
|
||||
"vega": "{{ base_url }}/vega@{{ vega_version }}?noext",
|
||||
"vega-lib": "{{ base_url }}/vega-lib?noext",
|
||||
"vega-lite": "{{ base_url }}/vega-lite@{{ vegalite_version }}?noext",
|
||||
"vega-embed": "{{ base_url }}/vega-embed@{{ vegaembed_version }}?noext",
|
||||
"olli": "{{ base_url }}/olli@{{ olli_version }}?noext",
|
||||
"olli-adapters": "{{ base_url }}/olli-adapters@{{ olli_adapters_version }}?noext",
|
||||
};
|
||||
|
||||
function maybeLoadScript(lib, version) {
|
||||
var key = `${lib.replace("-", "")}_version`;
|
||||
return (VEGA_DEBUG[key] == version) ?
|
||||
Promise.resolve(paths[lib]) :
|
||||
new Promise(function(resolve, reject) {
|
||||
var s = document.createElement('script');
|
||||
document.getElementsByTagName("head")[0].appendChild(s);
|
||||
s.async = true;
|
||||
s.onload = () => {
|
||||
VEGA_DEBUG[key] = version;
|
||||
return resolve(paths[lib]);
|
||||
};
|
||||
s.onerror = () => reject(`Error loading script: ${paths[lib]}`);
|
||||
s.src = paths[lib];
|
||||
});
|
||||
}
|
||||
|
||||
function showError(err) {
|
||||
outputDiv.innerHTML = `<div class="error" style="color:red;">${err}</div>`;
|
||||
throw err;
|
||||
}
|
||||
|
||||
function displayChart(vegaEmbed, olli, olliAdapters) {
|
||||
vegaEmbed(outputDiv, spec, embedOpt)
|
||||
.catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));
|
||||
olliAdapters.VegaLiteAdapter(spec).then(olliVisSpec => {
|
||||
const olliFunc = typeof olli === 'function' ? olli : olli.olli;
|
||||
const olliRender = olliFunc(olliVisSpec);
|
||||
olliDiv.append(olliRender);
|
||||
});
|
||||
}
|
||||
|
||||
if(typeof define === "function" && define.amd) {
|
||||
requirejs.config({paths});
|
||||
let deps = ["vega-embed", "olli", "olli-adapters"];
|
||||
require(deps, displayChart, err => showError(`Error loading script: ${err.message}`));
|
||||
} else {
|
||||
maybeLoadScript("vega", "{{vega_version}}")
|
||||
.then(() => maybeLoadScript("vega-lite", "{{vegalite_version}}"))
|
||||
.then(() => maybeLoadScript("vega-embed", "{{vegaembed_version}}"))
|
||||
.then(() => maybeLoadScript("olli", "{{olli_version}}"))
|
||||
.then(() => maybeLoadScript("olli-adapters", "{{olli_adapters_version}}"))
|
||||
.catch(showError)
|
||||
.then(() => displayChart(vegaEmbed, olli, OlliAdapters));
|
||||
}
|
||||
})({{ spec }}, {{ embed_options }});
|
||||
</script>
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
TEMPLATES: dict[TemplateName, jinja2.Template] = {
|
||||
"standard": HTML_TEMPLATE,
|
||||
"universal": HTML_TEMPLATE_UNIVERSAL,
|
||||
"inline": INLINE_HTML_TEMPLATE,
|
||||
"olli": HTML_TEMPLATE_OLLI,
|
||||
}
|
||||
|
||||
|
||||
def spec_to_html(
|
||||
spec: dict[str, Any],
|
||||
mode: RenderMode,
|
||||
vega_version: str | None,
|
||||
vegaembed_version: str | None,
|
||||
vegalite_version: str | None = None,
|
||||
base_url: str = "https://cdn.jsdelivr.net/npm",
|
||||
output_div: str = "vis",
|
||||
embed_options: dict[str, Any] | None = None,
|
||||
json_kwds: dict[str, Any] | None = None,
|
||||
fullhtml: bool = True,
|
||||
requirejs: bool = False,
|
||||
template: jinja2.Template | TemplateName = "standard",
|
||||
) -> str:
|
||||
"""
|
||||
Embed a Vega/Vega-Lite spec into an HTML page.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spec : dict
|
||||
a dictionary representing a vega-lite plot spec.
|
||||
mode : string {'vega' | 'vega-lite'}
|
||||
The rendering mode. This value is overridden by embed_options['mode'],
|
||||
if it is present.
|
||||
vega_version : string
|
||||
For html output, the version of vega.js to use.
|
||||
vegalite_version : string
|
||||
For html output, the version of vegalite.js to use.
|
||||
vegaembed_version : string
|
||||
For html output, the version of vegaembed.js to use.
|
||||
base_url : string (optional)
|
||||
The base url from which to load the javascript libraries.
|
||||
output_div : string (optional)
|
||||
The id of the div element where the plot will be shown.
|
||||
embed_options : dict (optional)
|
||||
Dictionary of options to pass to the vega-embed script. Default
|
||||
entry is {'mode': mode}.
|
||||
json_kwds : dict (optional)
|
||||
Dictionary of keywords to pass to json.dumps().
|
||||
fullhtml : boolean (optional)
|
||||
If True (default) then return a full html page. If False, then return
|
||||
an HTML snippet that can be embedded into an HTML page.
|
||||
requirejs : boolean (optional)
|
||||
If False (default) then load libraries from base_url using <script>
|
||||
tags. If True, then load libraries using requirejs
|
||||
template : jinja2.Template or string (optional)
|
||||
Specify the template to use (default = 'standard'). If template is a
|
||||
string, it must be one of {'universal', 'standard', 'inline'}. Otherwise, it
|
||||
can be a jinja2.Template object containing a custom template.
|
||||
|
||||
Returns
|
||||
-------
|
||||
output : string
|
||||
an HTML string for rendering the chart.
|
||||
"""
|
||||
embed_options = embed_options or {}
|
||||
json_kwds = json_kwds or {}
|
||||
|
||||
mode = embed_options.setdefault("mode", mode)
|
||||
|
||||
if mode not in {"vega", "vega-lite"}:
|
||||
msg = "mode must be either 'vega' or 'vega-lite'"
|
||||
raise ValueError(msg)
|
||||
|
||||
if vega_version is None:
|
||||
msg = "must specify vega_version"
|
||||
raise ValueError(msg)
|
||||
|
||||
if vegaembed_version is None:
|
||||
msg = "must specify vegaembed_version"
|
||||
raise ValueError(msg)
|
||||
|
||||
if mode == "vega-lite" and vegalite_version is None:
|
||||
msg = "must specify vega-lite version for mode='vega-lite'"
|
||||
raise ValueError(msg)
|
||||
|
||||
render_kwargs = {}
|
||||
if template == "inline":
|
||||
vlc = import_vl_convert()
|
||||
vl_version = vl_version_for_vl_convert()
|
||||
render_kwargs["vegaembed_script"] = vlc.javascript_bundle(vl_version=vl_version)
|
||||
elif template == "olli":
|
||||
OLLI_VERSION = "2"
|
||||
OLLI_ADAPTERS_VERSION = "2"
|
||||
render_kwargs["olli_version"] = OLLI_VERSION
|
||||
render_kwargs["olli_adapters_version"] = OLLI_ADAPTERS_VERSION
|
||||
|
||||
jinja_template = TEMPLATES.get(template, template) # type: ignore[arg-type]
|
||||
if not hasattr(jinja_template, "render"):
|
||||
msg = f"Invalid template: {jinja_template}"
|
||||
raise ValueError(msg)
|
||||
|
||||
return jinja_template.render(
|
||||
spec=json.dumps(spec, **json_kwds),
|
||||
embed_options=json.dumps(embed_options),
|
||||
mode=mode,
|
||||
vega_version=vega_version,
|
||||
vegalite_version=vegalite_version,
|
||||
vegaembed_version=vegaembed_version,
|
||||
base_url=base_url,
|
||||
output_div=output_div,
|
||||
fullhtml=fullhtml,
|
||||
requirejs=requirejs,
|
||||
**render_kwargs,
|
||||
)
|
||||
377
myenv/lib/python3.11/site-packages/altair/utils/mimebundle.py
Normal file
377
myenv/lib/python3.11/site-packages/altair/utils/mimebundle.py
Normal file
@@ -0,0 +1,377 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import struct
|
||||
from typing import TYPE_CHECKING, Any, Literal, cast, overload
|
||||
|
||||
from ._importers import import_vl_convert, vl_version_for_vl_convert
|
||||
from .html import spec_to_html
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
MimeBundleFormat: TypeAlias = Literal[
|
||||
"html", "json", "png", "svg", "pdf", "vega", "vega-lite"
|
||||
]
|
||||
|
||||
|
||||
@overload
|
||||
def spec_to_mimebundle(
|
||||
spec: dict[str, Any],
|
||||
format: Literal["json", "vega-lite"],
|
||||
mode: Literal["vega-lite"] | None = ...,
|
||||
vega_version: str | None = ...,
|
||||
vegaembed_version: str | None = ...,
|
||||
vegalite_version: str | None = ...,
|
||||
embed_options: dict[str, Any] | None = ...,
|
||||
engine: Literal["vl-convert"] | None = ...,
|
||||
**kwargs,
|
||||
) -> dict[str, dict[str, Any]]: ...
|
||||
@overload
|
||||
def spec_to_mimebundle(
|
||||
spec: dict[str, Any],
|
||||
format: Literal["html"],
|
||||
mode: Literal["vega-lite"] | None = ...,
|
||||
vega_version: str | None = ...,
|
||||
vegaembed_version: str | None = ...,
|
||||
vegalite_version: str | None = ...,
|
||||
embed_options: dict[str, Any] | None = ...,
|
||||
engine: Literal["vl-convert"] | None = ...,
|
||||
**kwargs,
|
||||
) -> dict[str, str]: ...
|
||||
@overload
|
||||
def spec_to_mimebundle(
|
||||
spec: dict[str, Any],
|
||||
format: Literal["pdf", "svg", "vega"],
|
||||
mode: Literal["vega-lite"] | None = ...,
|
||||
vega_version: str | None = ...,
|
||||
vegaembed_version: str | None = ...,
|
||||
vegalite_version: str | None = ...,
|
||||
embed_options: dict[str, Any] | None = ...,
|
||||
engine: Literal["vl-convert"] | None = ...,
|
||||
**kwargs,
|
||||
) -> dict[str, Any]: ...
|
||||
@overload
|
||||
def spec_to_mimebundle(
|
||||
spec: dict[str, Any],
|
||||
format: Literal["png"],
|
||||
mode: Literal["vega-lite"] | None = ...,
|
||||
vega_version: str | None = ...,
|
||||
vegaembed_version: str | None = ...,
|
||||
vegalite_version: str | None = ...,
|
||||
embed_options: dict[str, Any] | None = ...,
|
||||
engine: Literal["vl-convert"] | None = ...,
|
||||
**kwargs,
|
||||
) -> tuple[dict[str, Any], dict[str, Any]]: ...
|
||||
def spec_to_mimebundle(
|
||||
spec: dict[str, Any],
|
||||
format: MimeBundleFormat,
|
||||
mode: Literal["vega-lite"] | None = None,
|
||||
vega_version: str | None = None,
|
||||
vegaembed_version: str | None = None,
|
||||
vegalite_version: str | None = None,
|
||||
embed_options: dict[str, Any] | None = None,
|
||||
engine: Literal["vl-convert"] | None = None,
|
||||
**kwargs,
|
||||
) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any]]:
|
||||
"""
|
||||
Convert a vega-lite specification to a mimebundle.
|
||||
|
||||
The mimebundle type is controlled by the ``format`` argument, which can be
|
||||
one of the following ['html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite']
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spec : dict
|
||||
a dictionary representing a vega-lite plot spec
|
||||
format : string {'html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite'}
|
||||
the file format to be saved.
|
||||
mode : string {'vega-lite'}
|
||||
The rendering mode.
|
||||
vega_version : string
|
||||
The version of vega.js to use
|
||||
vegaembed_version : string
|
||||
The version of vegaembed.js to use
|
||||
vegalite_version : string
|
||||
The version of vegalite.js to use. Only required if mode=='vega-lite'
|
||||
embed_options : dict (optional)
|
||||
The vegaEmbed options dictionary. Defaults to the embed options set with
|
||||
alt.renderers.set_embed_options().
|
||||
(See https://github.com/vega/vega-embed for details)
|
||||
engine: string {'vl-convert'}
|
||||
the conversion engine to use for 'png', 'svg', 'pdf', and 'vega' formats
|
||||
**kwargs :
|
||||
Additional arguments will be passed to the generating function
|
||||
|
||||
Returns
|
||||
-------
|
||||
output : dict
|
||||
a mime-bundle representing the image
|
||||
|
||||
Note
|
||||
----
|
||||
The png, svg, pdf, and vega outputs require the vl-convert package
|
||||
"""
|
||||
# Local import to avoid circular ImportError
|
||||
from altair import renderers
|
||||
from altair.utils.display import compile_with_vegafusion, using_vegafusion
|
||||
|
||||
if mode != "vega-lite":
|
||||
msg = "mode must be 'vega-lite'"
|
||||
raise ValueError(msg)
|
||||
|
||||
internal_mode: Literal["vega-lite", "vega"] = mode
|
||||
if using_vegafusion():
|
||||
spec = compile_with_vegafusion(spec)
|
||||
internal_mode = "vega"
|
||||
|
||||
# Default to the embed options set by alt.renderers.set_embed_options
|
||||
if embed_options is None:
|
||||
final_embed_options = renderers.options.get("embed_options", {})
|
||||
else:
|
||||
final_embed_options = embed_options
|
||||
|
||||
embed_options = preprocess_embed_options(final_embed_options)
|
||||
|
||||
if format in {"png", "svg", "pdf", "vega"}:
|
||||
return _spec_to_mimebundle_with_engine(
|
||||
spec,
|
||||
cast(Literal["png", "svg", "pdf", "vega"], format),
|
||||
internal_mode,
|
||||
engine=engine,
|
||||
format_locale=embed_options.get("formatLocale", None),
|
||||
time_format_locale=embed_options.get("timeFormatLocale", None),
|
||||
**kwargs,
|
||||
)
|
||||
elif format == "html":
|
||||
html = spec_to_html(
|
||||
spec,
|
||||
mode=internal_mode,
|
||||
vega_version=vega_version,
|
||||
vegaembed_version=vegaembed_version,
|
||||
vegalite_version=vegalite_version,
|
||||
embed_options=embed_options,
|
||||
**kwargs,
|
||||
)
|
||||
return {"text/html": html}
|
||||
elif format == "vega-lite":
|
||||
if vegalite_version is None:
|
||||
msg = "Must specify vegalite_version"
|
||||
raise ValueError(msg)
|
||||
return {f"application/vnd.vegalite.v{vegalite_version[0]}+json": spec}
|
||||
elif format == "json":
|
||||
return {"application/json": spec}
|
||||
else:
|
||||
msg = (
|
||||
"format must be one of "
|
||||
"['html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite']"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def _spec_to_mimebundle_with_engine(
|
||||
spec: dict,
|
||||
format: Literal["png", "svg", "pdf", "vega"],
|
||||
mode: Literal["vega-lite", "vega"],
|
||||
format_locale: str | dict | None = None,
|
||||
time_format_locale: str | dict | None = None,
|
||||
**kwargs,
|
||||
) -> Any:
|
||||
"""
|
||||
Helper for Vega-Lite to mimebundle conversions that require an engine.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spec : dict
|
||||
a dictionary representing a vega-lite plot spec
|
||||
format : string {'png', 'svg', 'pdf', 'vega'}
|
||||
the format of the mimebundle to be returned
|
||||
mode : string {'vega-lite', 'vega'}
|
||||
The rendering mode.
|
||||
engine: string {'vl-convert'}
|
||||
the conversion engine to use
|
||||
format_locale : str or dict
|
||||
d3-format locale name or dictionary. Defaults to "en-US" for United States English.
|
||||
See https://github.com/d3/d3-format/tree/main/locale for available names and example
|
||||
definitions.
|
||||
time_format_locale : str or dict
|
||||
d3-time-format locale name or dictionary. Defaults to "en-US" for United States English.
|
||||
See https://github.com/d3/d3-time-format/tree/main/locale for available names and example
|
||||
definitions.
|
||||
**kwargs :
|
||||
Additional arguments will be passed to the conversion function
|
||||
"""
|
||||
# Normalize the engine string (if any) by lower casing
|
||||
# and removing underscores and hyphens
|
||||
engine = kwargs.pop("engine", None)
|
||||
normalized_engine = _validate_normalize_engine(engine, format)
|
||||
|
||||
if normalized_engine == "vlconvert":
|
||||
vlc = import_vl_convert()
|
||||
vl_version = vl_version_for_vl_convert()
|
||||
if format == "vega":
|
||||
if mode == "vega":
|
||||
vg = spec
|
||||
else:
|
||||
vg = vlc.vegalite_to_vega(spec, vl_version=vl_version)
|
||||
return {"application/vnd.vega.v5+json": vg}
|
||||
elif format == "svg":
|
||||
if mode == "vega":
|
||||
svg = vlc.vega_to_svg(
|
||||
spec,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
else:
|
||||
svg = vlc.vegalite_to_svg(
|
||||
spec,
|
||||
vl_version=vl_version,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
return {"image/svg+xml": svg}
|
||||
elif format == "png":
|
||||
scale = kwargs.get("scale_factor", 1)
|
||||
# The default ppi for a PNG file is 72
|
||||
default_ppi = 72
|
||||
ppi = kwargs.get("ppi", default_ppi)
|
||||
if mode == "vega":
|
||||
png = vlc.vega_to_png(
|
||||
spec,
|
||||
scale=scale,
|
||||
ppi=ppi,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
else:
|
||||
png = vlc.vegalite_to_png(
|
||||
spec,
|
||||
vl_version=vl_version,
|
||||
scale=scale,
|
||||
ppi=ppi,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
factor = ppi / default_ppi
|
||||
w, h = _pngxy(png)
|
||||
return {"image/png": png}, {
|
||||
"image/png": {"width": w / factor, "height": h / factor}
|
||||
}
|
||||
elif format == "pdf":
|
||||
scale = kwargs.get("scale_factor", 1)
|
||||
if mode == "vega":
|
||||
pdf = vlc.vega_to_pdf(
|
||||
spec,
|
||||
scale=scale,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
else:
|
||||
pdf = vlc.vegalite_to_pdf(
|
||||
spec,
|
||||
vl_version=vl_version,
|
||||
scale=scale,
|
||||
format_locale=format_locale,
|
||||
time_format_locale=time_format_locale,
|
||||
)
|
||||
return {"application/pdf": pdf}
|
||||
else:
|
||||
# This should be validated above
|
||||
# but raise exception for the sake of future development
|
||||
msg = f"Unexpected format {format!r}"
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
# This should be validated above
|
||||
# but raise exception for the sake of future development
|
||||
msg = f"Unexpected normalized_engine {normalized_engine!r}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def _validate_normalize_engine(
|
||||
engine: Literal["vl-convert"] | None,
|
||||
format: Literal["png", "svg", "pdf", "vega"],
|
||||
) -> str:
|
||||
"""
|
||||
Helper to validate and normalize the user-provided engine.
|
||||
|
||||
engine : {None, 'vl-convert'}
|
||||
the user-provided engine string
|
||||
format : string {'png', 'svg', 'pdf', 'vega'}
|
||||
the format of the mimebundle to be returned
|
||||
"""
|
||||
# Try to import vl_convert
|
||||
try:
|
||||
vlc = import_vl_convert()
|
||||
except ImportError:
|
||||
vlc = None
|
||||
|
||||
# Normalize engine string by lower casing and removing underscores and hyphens
|
||||
normalized_engine = (
|
||||
None if engine is None else engine.lower().replace("-", "").replace("_", "")
|
||||
)
|
||||
|
||||
# Validate or infer default value of normalized_engine
|
||||
if normalized_engine == "vlconvert":
|
||||
if vlc is None:
|
||||
msg = "The 'vl-convert' conversion engine requires the vl-convert-python package"
|
||||
raise ValueError(msg)
|
||||
elif normalized_engine is None:
|
||||
if vlc is not None:
|
||||
normalized_engine = "vlconvert"
|
||||
else:
|
||||
msg = (
|
||||
f"Saving charts in {format!r} format requires the vl-convert-python package: "
|
||||
"see https://altair-viz.github.io/user_guide/saving_charts.html#png-svg-and-pdf-format"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
msg = f"Invalid conversion engine {engine!r}. Expected vl-convert"
|
||||
raise ValueError(msg)
|
||||
return normalized_engine
|
||||
|
||||
|
||||
def _pngxy(data):
|
||||
"""
|
||||
read the (width, height) from a PNG header.
|
||||
|
||||
Taken from IPython.display
|
||||
"""
|
||||
ihdr = data.index(b"IHDR")
|
||||
# next 8 bytes are width/height
|
||||
return struct.unpack(">ii", data[ihdr + 4 : ihdr + 12])
|
||||
|
||||
|
||||
def preprocess_embed_options(embed_options: dict) -> dict:
|
||||
"""
|
||||
Preprocess embed options to a form compatible with Vega Embed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
embed_options : dict
|
||||
The embed options dictionary to preprocess.
|
||||
|
||||
Returns
|
||||
-------
|
||||
embed_opts : dict
|
||||
The preprocessed embed options dictionary.
|
||||
"""
|
||||
embed_options = (embed_options or {}).copy()
|
||||
|
||||
# Convert locale strings to objects compatible with Vega Embed using vl-convert
|
||||
format_locale = embed_options.get("formatLocale", None)
|
||||
if isinstance(format_locale, str):
|
||||
vlc = import_vl_convert()
|
||||
embed_options["formatLocale"] = vlc.get_format_locale(format_locale)
|
||||
|
||||
time_format_locale = embed_options.get("timeFormatLocale", None)
|
||||
if isinstance(time_format_locale, str):
|
||||
vlc = import_vl_convert()
|
||||
embed_options["timeFormatLocale"] = vlc.get_time_format_locale(
|
||||
time_format_locale
|
||||
)
|
||||
|
||||
return embed_options
|
||||
@@ -0,0 +1,290 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from functools import partial
|
||||
from importlib.metadata import entry_points
|
||||
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, cast
|
||||
|
||||
from altair.utils.deprecation import deprecated_warn
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeIs
|
||||
else:
|
||||
from typing_extensions import TypeIs
|
||||
if sys.version_info >= (3, 12):
|
||||
from typing import TypeAliasType
|
||||
else:
|
||||
from typing_extensions import TypeAliasType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from types import TracebackType
|
||||
|
||||
T = TypeVar("T")
|
||||
R = TypeVar("R")
|
||||
Plugin = TypeAliasType("Plugin", Callable[..., R], type_params=(R,))
|
||||
PluginT = TypeVar("PluginT", bound=Plugin[Any])
|
||||
IsPlugin = Callable[[object], TypeIs[Plugin[Any]]]
|
||||
|
||||
|
||||
def _is_type(tp: type[T], /) -> Callable[[object], TypeIs[type[T]]]:
|
||||
"""
|
||||
Converts a type to guard function.
|
||||
|
||||
Added for compatibility with original `PluginRegistry` default.
|
||||
"""
|
||||
|
||||
def func(obj: object, /) -> TypeIs[type[T]]:
|
||||
return isinstance(obj, tp)
|
||||
|
||||
return func
|
||||
|
||||
|
||||
class NoSuchEntryPoint(Exception):
|
||||
def __init__(self, group, name):
|
||||
self.group = group
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return f"No {self.name!r} entry point found in group {self.group!r}"
|
||||
|
||||
|
||||
class PluginEnabler(Generic[PluginT, R]):
|
||||
"""
|
||||
Context manager for enabling plugins.
|
||||
|
||||
This object lets you use enable() as a context manager to
|
||||
temporarily enable a given plugin::
|
||||
|
||||
with plugins.enable("name"):
|
||||
do_something() # 'name' plugin temporarily enabled
|
||||
# plugins back to original state
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, registry: PluginRegistry[PluginT, R], name: str, **options: Any
|
||||
) -> None:
|
||||
self.registry: PluginRegistry[PluginT, R] = registry
|
||||
self.name: str = name
|
||||
self.options: dict[str, Any] = options
|
||||
self.original_state: dict[str, Any] = registry._get_state()
|
||||
self.registry._enable(name, **options)
|
||||
|
||||
def __enter__(self) -> PluginEnabler[PluginT, R]:
|
||||
return self
|
||||
|
||||
def __exit__(self, typ: type, value: Exception, traceback: TracebackType) -> None:
|
||||
self.registry._set_state(self.original_state)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self.registry).__name__}.enable({self.name!r})"
|
||||
|
||||
|
||||
class PluginRegistry(Generic[PluginT, R]):
|
||||
"""
|
||||
A registry for plugins.
|
||||
|
||||
This is a plugin registry that allows plugins to be loaded/registered
|
||||
in two ways:
|
||||
|
||||
1. Through an explicit call to ``.register(name, value)``.
|
||||
2. By looking for other Python packages that are installed and provide
|
||||
a setuptools entry point group.
|
||||
|
||||
When you create an instance of this class, provide the name of the
|
||||
entry point group to use::
|
||||
|
||||
reg = PluginRegister("my_entrypoint_group")
|
||||
|
||||
"""
|
||||
|
||||
# this is a mapping of name to error message to allow custom error messages
|
||||
# in case an entrypoint is not found
|
||||
entrypoint_err_messages: dict[str, str] = {}
|
||||
|
||||
# global settings is a key-value mapping of settings that are stored globally
|
||||
# in the registry rather than passed to the plugins
|
||||
_global_settings: dict[str, Any] = {}
|
||||
|
||||
def __init__(
|
||||
self, entry_point_group: str = "", plugin_type: IsPlugin = callable
|
||||
) -> None:
|
||||
"""
|
||||
Create a PluginRegistry for a named entry point group.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
entry_point_group: str
|
||||
The name of the entry point group.
|
||||
plugin_type
|
||||
A type narrowing function that will optionally be used for runtime
|
||||
type checking loaded plugins.
|
||||
|
||||
References
|
||||
----------
|
||||
https://typing.readthedocs.io/en/latest/spec/narrowing.html
|
||||
"""
|
||||
self.entry_point_group: str = entry_point_group
|
||||
self.plugin_type: IsPlugin
|
||||
if plugin_type is not callable and isinstance(plugin_type, type):
|
||||
msg: Any = (
|
||||
f"Pass a callable `TypeIs` function to `plugin_type` instead.\n"
|
||||
f"{type(self).__name__!r}(plugin_type)\n\n"
|
||||
f"See also:\n"
|
||||
f"https://typing.readthedocs.io/en/latest/spec/narrowing.html\n"
|
||||
f"https://docs.astral.sh/ruff/rules/assert/"
|
||||
)
|
||||
deprecated_warn(msg, version="5.4.0")
|
||||
self.plugin_type = cast(IsPlugin, _is_type(plugin_type))
|
||||
else:
|
||||
self.plugin_type = plugin_type
|
||||
self._active: Plugin[R] | None = None
|
||||
self._active_name: str = ""
|
||||
self._plugins: dict[str, PluginT] = {}
|
||||
self._options: dict[str, Any] = {}
|
||||
self._global_settings: dict[str, Any] = self.__class__._global_settings.copy()
|
||||
|
||||
def register(self, name: str, value: PluginT | None) -> PluginT | None:
|
||||
"""
|
||||
Register a plugin by name and value.
|
||||
|
||||
This method is used for explicit registration of a plugin and shouldn't be
|
||||
used to manage entry point managed plugins, which are auto-loaded.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
The name of the plugin.
|
||||
value: PluginType or None
|
||||
The actual plugin object to register or None to unregister that plugin.
|
||||
|
||||
Returns
|
||||
-------
|
||||
plugin: PluginType or None
|
||||
The plugin that was registered or unregistered.
|
||||
"""
|
||||
if value is None:
|
||||
return self._plugins.pop(name, None)
|
||||
elif self.plugin_type(value):
|
||||
self._plugins[name] = value
|
||||
return value
|
||||
else:
|
||||
msg = f"{type(value).__name__!r} is not compatible with {type(self).__name__!r}"
|
||||
raise TypeError(msg)
|
||||
|
||||
def names(self) -> list[str]:
|
||||
"""List the names of the registered and entry points plugins."""
|
||||
exts = list(self._plugins.keys())
|
||||
e_points = importlib_metadata_get(self.entry_point_group)
|
||||
more_exts = [ep.name for ep in e_points]
|
||||
exts.extend(more_exts)
|
||||
return sorted(set(exts))
|
||||
|
||||
def _get_state(self) -> dict[str, Any]:
|
||||
"""Return a dictionary representing the current state of the registry."""
|
||||
return {
|
||||
"_active": self._active,
|
||||
"_active_name": self._active_name,
|
||||
"_plugins": self._plugins.copy(),
|
||||
"_options": self._options.copy(),
|
||||
"_global_settings": self._global_settings.copy(),
|
||||
}
|
||||
|
||||
def _set_state(self, state: dict[str, Any]) -> None:
|
||||
"""Reset the state of the registry."""
|
||||
assert set(state.keys()) == {
|
||||
"_active",
|
||||
"_active_name",
|
||||
"_plugins",
|
||||
"_options",
|
||||
"_global_settings",
|
||||
}
|
||||
for key, val in state.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
def _enable(self, name: str, **options) -> None:
|
||||
if name not in self._plugins:
|
||||
try:
|
||||
(ep,) = (
|
||||
ep
|
||||
for ep in importlib_metadata_get(self.entry_point_group)
|
||||
if ep.name == name
|
||||
)
|
||||
except ValueError as err:
|
||||
if name in self.entrypoint_err_messages:
|
||||
raise ValueError(self.entrypoint_err_messages[name]) from err
|
||||
else:
|
||||
raise NoSuchEntryPoint(self.entry_point_group, name) from err
|
||||
value = cast(PluginT, ep.load())
|
||||
self.register(name, value)
|
||||
self._active_name = name
|
||||
self._active = self._plugins[name]
|
||||
for key in set(options.keys()) & set(self._global_settings.keys()):
|
||||
self._global_settings[key] = options.pop(key)
|
||||
self._options = options
|
||||
|
||||
def enable(
|
||||
self, name: str | None = None, **options: Any
|
||||
) -> PluginEnabler[PluginT, R]:
|
||||
"""
|
||||
Enable a plugin by name.
|
||||
|
||||
This can be either called directly, or used as a context manager.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : string (optional)
|
||||
The name of the plugin to enable. If not specified, then use the
|
||||
current active name.
|
||||
**options :
|
||||
Any additional parameters will be passed to the plugin as keyword
|
||||
arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
PluginEnabler:
|
||||
An object that allows enable() to be used as a context manager
|
||||
"""
|
||||
if name is None:
|
||||
name = self.active
|
||||
return PluginEnabler(self, name, **options)
|
||||
|
||||
@property
|
||||
def active(self) -> str:
|
||||
"""Return the name of the currently active plugin."""
|
||||
return self._active_name
|
||||
|
||||
@property
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return the current options dictionary."""
|
||||
return self._options
|
||||
|
||||
def get(self) -> partial[R] | Plugin[R] | None:
|
||||
"""Return the currently active plugin."""
|
||||
if (func := self._active) and self.plugin_type(func):
|
||||
return partial(func, **self._options) if self._options else func
|
||||
elif self._active is not None:
|
||||
msg = (
|
||||
f"{type(self).__name__!r} requires all plugins to be callable objects, "
|
||||
f"but {type(self._active).__name__!r} is not callable."
|
||||
)
|
||||
raise TypeError(msg)
|
||||
elif TYPE_CHECKING:
|
||||
# NOTE: The `None` return is implicit, but `mypy` isn't satisfied
|
||||
# - `ruff` will factor out explicit `None` return
|
||||
# - `pyright` has no issue
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self).__name__}(active={self.active!r}, registered={self.names()!r})"
|
||||
|
||||
|
||||
def importlib_metadata_get(group):
|
||||
ep = entry_points()
|
||||
# 'select' was introduced in Python 3.10 and 'get' got deprecated
|
||||
# We don't check for Python version here as by checking with hasattr we
|
||||
# also get compatibility with the importlib_metadata package which had a different
|
||||
# deprecation cycle for 'get'
|
||||
if hasattr(ep, "select"):
|
||||
return ep.select(group=group) # pyright: ignore
|
||||
else:
|
||||
return ep.get(group, [])
|
||||
224
myenv/lib/python3.11/site-packages/altair/utils/save.py
Normal file
224
myenv/lib/python3.11/site-packages/altair/utils/save.py
Normal file
@@ -0,0 +1,224 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import warnings
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal
|
||||
|
||||
from altair.utils._vegafusion_data import using_vegafusion
|
||||
from altair.utils.deprecation import deprecated_warn
|
||||
from altair.vegalite.v5.data import data_transformers
|
||||
|
||||
from .mimebundle import spec_to_mimebundle
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def write_file_or_filename(
|
||||
fp: str | Path | IO,
|
||||
content: str | bytes,
|
||||
mode: str = "w",
|
||||
encoding: str | None = None,
|
||||
) -> None:
|
||||
"""Write content to fp, whether fp is a string, a pathlib Path or a file-like object."""
|
||||
if isinstance(fp, (str, pathlib.Path)):
|
||||
with pathlib.Path(fp).open(mode=mode, encoding=encoding) as f:
|
||||
f.write(content)
|
||||
else:
|
||||
fp.write(content)
|
||||
|
||||
|
||||
def set_inspect_format_argument(
|
||||
format: str | None, fp: str | Path | IO, inline: bool
|
||||
) -> str:
|
||||
"""Inspect the format argument in the save function."""
|
||||
if format is None:
|
||||
if isinstance(fp, (str, pathlib.Path)):
|
||||
format = pathlib.Path(fp).suffix.lstrip(".")
|
||||
else:
|
||||
msg = (
|
||||
"must specify file format: "
|
||||
"['png', 'svg', 'pdf', 'html', 'json', 'vega']"
|
||||
)
|
||||
raise ValueError(msg)
|
||||
|
||||
if format != "html" and inline:
|
||||
warnings.warn("inline argument ignored for non HTML formats.", stacklevel=1)
|
||||
|
||||
return format
|
||||
|
||||
|
||||
def set_inspect_mode_argument(
|
||||
mode: Literal["vega-lite"] | None,
|
||||
embed_options: dict[str, Any],
|
||||
spec: dict[str, Any],
|
||||
vegalite_version: str | None,
|
||||
) -> Literal["vega-lite"]:
|
||||
"""Inspect the mode argument in the save function."""
|
||||
if mode is None:
|
||||
if "mode" in embed_options:
|
||||
mode = embed_options["mode"]
|
||||
elif "$schema" in spec:
|
||||
mode = spec["$schema"].split("/")[-2]
|
||||
else:
|
||||
mode = "vega-lite"
|
||||
|
||||
if mode != "vega-lite":
|
||||
msg = "mode must be 'vega-lite', " f"not '{mode}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
if mode == "vega-lite" and vegalite_version is None:
|
||||
msg = "must specify vega-lite version"
|
||||
raise ValueError(msg)
|
||||
|
||||
return mode
|
||||
|
||||
|
||||
def save(
|
||||
chart,
|
||||
fp: str | Path | IO,
|
||||
vega_version: str | None,
|
||||
vegaembed_version: str | None,
|
||||
format: Literal["json", "html", "png", "svg", "pdf"] | None = None,
|
||||
mode: Literal["vega-lite"] | None = None,
|
||||
vegalite_version: str | None = None,
|
||||
embed_options: dict | None = None,
|
||||
json_kwds: dict | None = None,
|
||||
scale_factor: float = 1,
|
||||
engine: Literal["vl-convert"] | None = None,
|
||||
inline: bool = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""
|
||||
Save a chart to file in a variety of formats.
|
||||
|
||||
Supported formats are [json, html, png, svg, pdf]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chart : alt.Chart
|
||||
the chart instance to save
|
||||
fp : string filename, pathlib.Path or file-like object
|
||||
file to which to write the chart.
|
||||
format : string (optional)
|
||||
the format to write: one of ['json', 'html', 'png', 'svg', 'pdf'].
|
||||
If not specified, the format will be determined from the filename.
|
||||
mode : string (optional)
|
||||
Must be 'vega-lite'. If not specified, then infer the mode from
|
||||
the '$schema' property of the spec, or the ``opt`` dictionary.
|
||||
If it's not specified in either of those places, then use 'vega-lite'.
|
||||
vega_version : string (optional)
|
||||
For html output, the version of vega.js to use
|
||||
vegalite_version : string (optional)
|
||||
For html output, the version of vegalite.js to use
|
||||
vegaembed_version : string (optional)
|
||||
For html output, the version of vegaembed.js to use
|
||||
embed_options : dict (optional)
|
||||
The vegaEmbed options dictionary. Default is {}
|
||||
(See https://github.com/vega/vega-embed for details)
|
||||
json_kwds : dict (optional)
|
||||
Additional keyword arguments are passed to the output method
|
||||
associated with the specified format.
|
||||
scale_factor : float (optional)
|
||||
scale_factor to use to change size/resolution of png or svg output
|
||||
engine: string {'vl-convert'}
|
||||
the conversion engine to use for 'png', 'svg', and 'pdf' formats
|
||||
inline: bool (optional)
|
||||
If False (default), the required JavaScript libraries are loaded
|
||||
from a CDN location in the resulting html file.
|
||||
If True, the required JavaScript libraries are inlined into the resulting
|
||||
html file so that it will work without an internet connection.
|
||||
The vl-convert-python package is required if True.
|
||||
**kwargs :
|
||||
additional kwargs passed to spec_to_mimebundle.
|
||||
"""
|
||||
if _ := kwargs.pop("webdriver", None):
|
||||
deprecated_warn(
|
||||
"The webdriver argument is not relevant for the new vl-convert engine which replaced altair_saver. "
|
||||
"The argument will be removed in a future release.",
|
||||
version="5.0.0",
|
||||
)
|
||||
|
||||
json_kwds = json_kwds or {}
|
||||
encoding = kwargs.get("encoding", "utf-8")
|
||||
format = set_inspect_format_argument(format, fp, inline) # type: ignore[assignment]
|
||||
|
||||
def perform_save() -> None:
|
||||
spec = chart.to_dict(context={"pre_transform": False})
|
||||
|
||||
inner_mode = set_inspect_mode_argument(
|
||||
mode, embed_options or {}, spec, vegalite_version
|
||||
)
|
||||
|
||||
if format == "json":
|
||||
json_spec = json.dumps(spec, **json_kwds)
|
||||
write_file_or_filename(fp, json_spec, mode="w", encoding=encoding)
|
||||
elif format == "html":
|
||||
if inline:
|
||||
kwargs["template"] = "inline"
|
||||
mb_html = spec_to_mimebundle(
|
||||
spec=spec,
|
||||
format=format,
|
||||
mode=inner_mode,
|
||||
vega_version=vega_version,
|
||||
vegalite_version=vegalite_version,
|
||||
vegaembed_version=vegaembed_version,
|
||||
embed_options=embed_options,
|
||||
json_kwds=json_kwds,
|
||||
**kwargs,
|
||||
)
|
||||
write_file_or_filename(
|
||||
fp, mb_html["text/html"], mode="w", encoding=encoding
|
||||
)
|
||||
elif format == "png":
|
||||
mb_png = spec_to_mimebundle(
|
||||
spec=spec,
|
||||
format=format,
|
||||
mode=inner_mode,
|
||||
vega_version=vega_version,
|
||||
vegalite_version=vegalite_version,
|
||||
vegaembed_version=vegaembed_version,
|
||||
embed_options=embed_options,
|
||||
scale_factor=scale_factor,
|
||||
engine=engine,
|
||||
**kwargs,
|
||||
)
|
||||
write_file_or_filename(fp, mb_png[0]["image/png"], mode="wb")
|
||||
elif format in {"svg", "pdf", "vega"}:
|
||||
mb_any = spec_to_mimebundle(
|
||||
spec=spec,
|
||||
format=format,
|
||||
mode=inner_mode,
|
||||
vega_version=vega_version,
|
||||
vegalite_version=vegalite_version,
|
||||
vegaembed_version=vegaembed_version,
|
||||
embed_options=embed_options,
|
||||
scale_factor=scale_factor,
|
||||
engine=engine,
|
||||
**kwargs,
|
||||
)
|
||||
if format == "pdf":
|
||||
write_file_or_filename(fp, mb_any["application/pdf"], mode="wb")
|
||||
else:
|
||||
write_file_or_filename(
|
||||
fp, mb_any["image/svg+xml"], mode="w", encoding=encoding
|
||||
)
|
||||
else:
|
||||
msg = f"Unsupported format: '{format}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
if using_vegafusion():
|
||||
# When the vegafusion data transformer is enabled, transforms will be
|
||||
# evaluated during save and the resulting data will be included in the
|
||||
# vega specification that is saved.
|
||||
with data_transformers.disable_max_rows():
|
||||
perform_save()
|
||||
else:
|
||||
# Temporarily turn off any data transformers so that all data is inlined
|
||||
# when calling chart.to_dict. This is relevant for vl-convert which cannot access
|
||||
# local json files which could be created by a json data transformer. Furthermore,
|
||||
# we don't exit the with statement until this function completed due to the issue
|
||||
# described at https://github.com/vega/vl-convert/issues/31
|
||||
with data_transformers.enable("default"), data_transformers.disable_max_rows():
|
||||
perform_save()
|
||||
1616
myenv/lib/python3.11/site-packages/altair/utils/schemapi.py
Normal file
1616
myenv/lib/python3.11/site-packages/altair/utils/schemapi.py
Normal file
File diff suppressed because it is too large
Load Diff
130
myenv/lib/python3.11/site-packages/altair/utils/selection.py
Normal file
130
myenv/lib/python3.11/site-packages/altair/utils/selection.py
Normal file
@@ -0,0 +1,130 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, NewType
|
||||
|
||||
# Type representing the "{selection}_store" dataset that corresponds to a
|
||||
# Vega-Lite selection
|
||||
Store = NewType("Store", list[dict[str, Any]])
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=True)
|
||||
class IndexSelection:
|
||||
"""
|
||||
Represents the state of an alt.selection_point() when neither the fields nor encodings arguments are specified.
|
||||
|
||||
The value field is a list of zero-based indices into the
|
||||
selected dataset.
|
||||
|
||||
Note: These indices only apply to the input DataFrame
|
||||
for charts that do not include aggregations (e.g. a scatter chart).
|
||||
"""
|
||||
|
||||
name: str
|
||||
value: list[int]
|
||||
store: Store
|
||||
|
||||
@staticmethod
|
||||
def from_vega(name: str, signal: dict[str, dict] | None, store: Store):
|
||||
"""
|
||||
Construct an IndexSelection from the raw Vega signal and dataset values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
The selection's name
|
||||
signal: dict or None
|
||||
The value of the Vega signal corresponding to the selection
|
||||
store: list
|
||||
The value of the Vega dataset corresponding to the selection.
|
||||
This dataset is named "{name}_store" in the Vega view.
|
||||
|
||||
Returns
|
||||
-------
|
||||
IndexSelection
|
||||
"""
|
||||
if signal is None:
|
||||
indices = []
|
||||
else:
|
||||
points = signal.get("vlPoint", {}).get("or", [])
|
||||
indices = [p["_vgsid_"] - 1 for p in points]
|
||||
return IndexSelection(name=name, value=indices, store=store)
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=True)
|
||||
class PointSelection:
|
||||
"""
|
||||
Represents the state of an alt.selection_point() when the fields or encodings arguments are specified.
|
||||
|
||||
The value field is a list of dicts of the form:
|
||||
[{"dim1": 1, "dim2": "A"}, {"dim1": 2, "dim2": "BB"}]
|
||||
|
||||
where "dim1" and "dim2" are dataset columns and the dict values
|
||||
correspond to the specific selected values.
|
||||
"""
|
||||
|
||||
name: str
|
||||
value: list[dict[str, Any]]
|
||||
store: Store
|
||||
|
||||
@staticmethod
|
||||
def from_vega(name: str, signal: dict[str, dict] | None, store: Store):
|
||||
"""
|
||||
Construct a PointSelection from the raw Vega signal and dataset values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
The selection's name
|
||||
signal: dict or None
|
||||
The value of the Vega signal corresponding to the selection
|
||||
store: list
|
||||
The value of the Vega dataset corresponding to the selection.
|
||||
This dataset is named "{name}_store" in the Vega view.
|
||||
|
||||
Returns
|
||||
-------
|
||||
PointSelection
|
||||
"""
|
||||
points = [] if signal is None else signal.get("vlPoint", {}).get("or", [])
|
||||
return PointSelection(name=name, value=points, store=store)
|
||||
|
||||
|
||||
@dataclass(frozen=True, eq=True)
|
||||
class IntervalSelection:
|
||||
"""
|
||||
Represents the state of an alt.selection_interval().
|
||||
|
||||
The value field is a dict of the form:
|
||||
{"dim1": [0, 10], "dim2": ["A", "BB", "CCC"]}
|
||||
|
||||
where "dim1" and "dim2" are dataset columns and the dict values
|
||||
correspond to the selected range.
|
||||
"""
|
||||
|
||||
name: str
|
||||
value: dict[str, list]
|
||||
store: Store
|
||||
|
||||
@staticmethod
|
||||
def from_vega(name: str, signal: dict[str, list] | None, store: Store):
|
||||
"""
|
||||
Construct an IntervalSelection from the raw Vega signal and dataset values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
The selection's name
|
||||
signal: dict or None
|
||||
The value of the Vega signal corresponding to the selection
|
||||
store: list
|
||||
The value of the Vega dataset corresponding to the selection.
|
||||
This dataset is named "{name}_store" in the Vega view.
|
||||
|
||||
Returns
|
||||
-------
|
||||
PointSelection
|
||||
"""
|
||||
if signal is None:
|
||||
signal = {}
|
||||
return IntervalSelection(name=name, value=signal, store=store)
|
||||
151
myenv/lib/python3.11/site-packages/altair/utils/server.py
Normal file
151
myenv/lib/python3.11/site-packages/altair/utils/server.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
A Simple server used to show altair graphics from a prompt or script.
|
||||
|
||||
This is adapted from the mpld3 package; see
|
||||
https://github.com/mpld3/mpld3/blob/master/mpld3/_server.py
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import random
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import webbrowser
|
||||
from http import server
|
||||
from io import BytesIO as IO
|
||||
|
||||
JUPYTER_WARNING = """
|
||||
Note: if you're in the Jupyter notebook, Chart.serve() is not the best
|
||||
way to view plots. Consider using Chart.display().
|
||||
You must interrupt the kernel to cancel this command.
|
||||
"""
|
||||
|
||||
|
||||
# Mock server used for testing
|
||||
|
||||
|
||||
class MockRequest:
|
||||
def makefile(self, *args, **kwargs):
|
||||
return IO(b"GET /")
|
||||
|
||||
def sendall(self, response):
|
||||
pass
|
||||
|
||||
|
||||
class MockServer:
|
||||
def __init__(self, ip_port, Handler):
|
||||
Handler(MockRequest(), ip_port[0], self)
|
||||
|
||||
def serve_forever(self):
|
||||
pass
|
||||
|
||||
def server_close(self):
|
||||
pass
|
||||
|
||||
|
||||
def generate_handler(html, files=None):
|
||||
if files is None:
|
||||
files = {}
|
||||
|
||||
class MyHandler(server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
"""Respond to a GET request."""
|
||||
if self.path == "/":
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
self.wfile.write(html.encode())
|
||||
elif self.path in files:
|
||||
content_type, content = files[self.path]
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", content_type)
|
||||
self.end_headers()
|
||||
self.wfile.write(content.encode())
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
return MyHandler
|
||||
|
||||
|
||||
def find_open_port(ip, port, n=50):
|
||||
"""Find an open port near the specified port."""
|
||||
ports = itertools.chain(
|
||||
(port + i for i in range(n)), (port + random.randint(-2 * n, 2 * n))
|
||||
)
|
||||
|
||||
for port in ports:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
result = s.connect_ex((ip, port))
|
||||
s.close()
|
||||
if result != 0:
|
||||
return port
|
||||
msg = "no open ports found"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def serve(
|
||||
html,
|
||||
ip="127.0.0.1",
|
||||
port=8888,
|
||||
n_retries=50,
|
||||
files=None,
|
||||
jupyter_warning=True,
|
||||
open_browser=True,
|
||||
http_server=None,
|
||||
) -> None:
|
||||
"""
|
||||
Start a server serving the given HTML, and (optionally) open a browser.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
html : string
|
||||
HTML to serve
|
||||
ip : string (default = '127.0.0.1')
|
||||
ip address at which the HTML will be served.
|
||||
port : int (default = 8888)
|
||||
the port at which to serve the HTML
|
||||
n_retries : int (default = 50)
|
||||
the number of nearby ports to search if the specified port is in use.
|
||||
files : dictionary (optional)
|
||||
dictionary of extra content to serve
|
||||
jupyter_warning : bool (optional)
|
||||
if True (default), then print a warning if this is used within Jupyter
|
||||
open_browser : bool (optional)
|
||||
if True (default), then open a web browser to the given HTML
|
||||
http_server : class (optional)
|
||||
optionally specify an HTTPServer class to use for showing the
|
||||
figure. The default is Python's basic HTTPServer.
|
||||
"""
|
||||
port = find_open_port(ip, port, n_retries)
|
||||
Handler = generate_handler(html, files)
|
||||
|
||||
if http_server is None:
|
||||
srvr = server.HTTPServer((ip, port), Handler)
|
||||
else:
|
||||
srvr = http_server((ip, port), Handler)
|
||||
|
||||
if jupyter_warning:
|
||||
try:
|
||||
__IPYTHON__ # type: ignore # noqa
|
||||
except NameError:
|
||||
pass
|
||||
else:
|
||||
print(JUPYTER_WARNING)
|
||||
|
||||
# Start the server
|
||||
print(f"Serving to http://{ip}:{port}/ [Ctrl-C to exit]")
|
||||
sys.stdout.flush()
|
||||
|
||||
if open_browser:
|
||||
# Use a thread to open a web browser pointing to the server
|
||||
def b():
|
||||
return webbrowser.open(f"http://{ip}:{port}")
|
||||
|
||||
threading.Thread(target=b).start()
|
||||
|
||||
try:
|
||||
srvr.serve_forever()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
print("\nstopping Server...")
|
||||
|
||||
srvr.server_close()
|
||||
@@ -0,0 +1,2 @@
|
||||
# ruff: noqa: F403
|
||||
from .v5 import *
|
||||
@@ -0,0 +1,2 @@
|
||||
# ruff: noqa
|
||||
from .v5.api import *
|
||||
66
myenv/lib/python3.11/site-packages/altair/vegalite/data.py
Normal file
66
myenv/lib/python3.11/site-packages/altair/vegalite/data.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, overload
|
||||
|
||||
from altair.utils.core import sanitize_pandas_dataframe
|
||||
from altair.utils.data import DataTransformerRegistry as _DataTransformerRegistry
|
||||
from altair.utils.data import (
|
||||
MaxRowsError,
|
||||
check_data_type,
|
||||
limit_rows,
|
||||
sample,
|
||||
to_csv,
|
||||
to_json,
|
||||
to_values,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from altair.utils.data import DataType, ToValuesReturnType
|
||||
from altair.utils.plugin_registry import PluginEnabler
|
||||
|
||||
|
||||
@overload
|
||||
def default_data_transformer(
|
||||
data: None = ..., max_rows: int = ...
|
||||
) -> Callable[[DataType], ToValuesReturnType]: ...
|
||||
@overload
|
||||
def default_data_transformer(
|
||||
data: DataType, max_rows: int = ...
|
||||
) -> ToValuesReturnType: ...
|
||||
def default_data_transformer(
|
||||
data: DataType | None = None, max_rows: int = 5000
|
||||
) -> Callable[[DataType], ToValuesReturnType] | ToValuesReturnType:
|
||||
if data is None:
|
||||
|
||||
def pipe(data: DataType, /) -> ToValuesReturnType:
|
||||
data = limit_rows(data, max_rows=max_rows)
|
||||
return to_values(data)
|
||||
|
||||
return pipe
|
||||
|
||||
else:
|
||||
return to_values(limit_rows(data, max_rows=max_rows))
|
||||
|
||||
|
||||
class DataTransformerRegistry(_DataTransformerRegistry):
|
||||
def disable_max_rows(self) -> PluginEnabler:
|
||||
"""Disable the MaxRowsError."""
|
||||
options = self.options
|
||||
if self.active in {"default", "vegafusion"}:
|
||||
options = options.copy()
|
||||
options["max_rows"] = None
|
||||
return self.enable(**options)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"DataTransformerRegistry",
|
||||
"MaxRowsError",
|
||||
"check_data_type",
|
||||
"default_data_transformer",
|
||||
"limit_rows",
|
||||
"sample",
|
||||
"sanitize_pandas_dataframe",
|
||||
"to_csv",
|
||||
"to_json",
|
||||
"to_values",
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
from altair.utils.display import (
|
||||
DefaultRendererReturnType,
|
||||
Displayable,
|
||||
HTMLRenderer,
|
||||
RendererRegistry,
|
||||
default_renderer_base,
|
||||
json_renderer_base,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"DefaultRendererReturnType",
|
||||
"Displayable",
|
||||
"HTMLRenderer",
|
||||
"RendererRegistry",
|
||||
"default_renderer_base",
|
||||
"json_renderer_base",
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
"""Altair schema wrappers."""
|
||||
|
||||
# ruff: noqa: F403
|
||||
from .v5.schema import *
|
||||
@@ -0,0 +1,652 @@
|
||||
# ruff: noqa: F401, F403, F405
|
||||
from altair.expr.core import datum
|
||||
from altair.vegalite.v5 import api, compiler, schema
|
||||
from altair.vegalite.v5.api import *
|
||||
from altair.vegalite.v5.compiler import vegalite_compilers
|
||||
from altair.vegalite.v5.data import (
|
||||
MaxRowsError,
|
||||
data_transformers,
|
||||
default_data_transformer,
|
||||
limit_rows,
|
||||
sample,
|
||||
to_csv,
|
||||
to_json,
|
||||
to_values,
|
||||
)
|
||||
from altair.vegalite.v5.display import (
|
||||
VEGA_VERSION,
|
||||
VEGAEMBED_VERSION,
|
||||
VEGALITE_VERSION,
|
||||
VegaLite,
|
||||
renderers,
|
||||
)
|
||||
from altair.vegalite.v5.schema import *
|
||||
|
||||
# The content of __all__ is automatically written by
|
||||
# tools/update_init_file.py. Do not modify directly.
|
||||
|
||||
__all__ = [
|
||||
"SCHEMA_URL",
|
||||
"SCHEMA_VERSION",
|
||||
"TOPLEVEL_ONLY_KEYS",
|
||||
"URI",
|
||||
"VEGAEMBED_VERSION",
|
||||
"VEGALITE_VERSION",
|
||||
"VEGA_VERSION",
|
||||
"X2",
|
||||
"Y2",
|
||||
"Aggregate",
|
||||
"AggregateOp",
|
||||
"AggregateTransform",
|
||||
"AggregatedFieldDef",
|
||||
"Align",
|
||||
"AllSortString",
|
||||
"Angle",
|
||||
"AngleDatum",
|
||||
"AngleValue",
|
||||
"AnyMark",
|
||||
"AnyMarkConfig",
|
||||
"AreaConfig",
|
||||
"ArgmaxDef",
|
||||
"ArgminDef",
|
||||
"AutoSizeParams",
|
||||
"AutosizeType",
|
||||
"Axis",
|
||||
"AxisConfig",
|
||||
"AxisOrient",
|
||||
"AxisResolveMap",
|
||||
"BBox",
|
||||
"BarConfig",
|
||||
"BaseTitleNoValueRefs",
|
||||
"Baseline",
|
||||
"Bin",
|
||||
"BinExtent",
|
||||
"BinParams",
|
||||
"BinTransform",
|
||||
"BindCheckbox",
|
||||
"BindDirect",
|
||||
"BindInput",
|
||||
"BindRadioSelect",
|
||||
"BindRange",
|
||||
"Binding",
|
||||
"BinnedTimeUnit",
|
||||
"Blend",
|
||||
"BoxPlot",
|
||||
"BoxPlotConfig",
|
||||
"BoxPlotDef",
|
||||
"BrushConfig",
|
||||
"CalculateTransform",
|
||||
"Categorical",
|
||||
"ChainedWhen",
|
||||
"Chart",
|
||||
"ChartDataType",
|
||||
"Color",
|
||||
"ColorDatum",
|
||||
"ColorDef",
|
||||
"ColorName",
|
||||
"ColorScheme",
|
||||
"ColorValue",
|
||||
"Column",
|
||||
"CompositeMark",
|
||||
"CompositeMarkDef",
|
||||
"CompositionConfig",
|
||||
"ConcatChart",
|
||||
"ConcatSpecGenericSpec",
|
||||
"ConditionalAxisColor",
|
||||
"ConditionalAxisLabelAlign",
|
||||
"ConditionalAxisLabelBaseline",
|
||||
"ConditionalAxisLabelFontStyle",
|
||||
"ConditionalAxisLabelFontWeight",
|
||||
"ConditionalAxisNumber",
|
||||
"ConditionalAxisNumberArray",
|
||||
"ConditionalAxisPropertyAlignnull",
|
||||
"ConditionalAxisPropertyColornull",
|
||||
"ConditionalAxisPropertyFontStylenull",
|
||||
"ConditionalAxisPropertyFontWeightnull",
|
||||
"ConditionalAxisPropertyTextBaselinenull",
|
||||
"ConditionalAxisPropertynumberArraynull",
|
||||
"ConditionalAxisPropertynumbernull",
|
||||
"ConditionalAxisPropertystringnull",
|
||||
"ConditionalAxisString",
|
||||
"ConditionalMarkPropFieldOrDatumDef",
|
||||
"ConditionalMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDef",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterStringFieldDef",
|
||||
"ConditionalParameterValueDefGradientstringnullExprRef",
|
||||
"ConditionalParameterValueDefTextExprRef",
|
||||
"ConditionalParameterValueDefnumber",
|
||||
"ConditionalParameterValueDefnumberArrayExprRef",
|
||||
"ConditionalParameterValueDefnumberExprRef",
|
||||
"ConditionalParameterValueDefstringExprRef",
|
||||
"ConditionalParameterValueDefstringnullExprRef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalPredicateStringFieldDef",
|
||||
"ConditionalPredicateValueDefAlignnullExprRef",
|
||||
"ConditionalPredicateValueDefColornullExprRef",
|
||||
"ConditionalPredicateValueDefFontStylenullExprRef",
|
||||
"ConditionalPredicateValueDefFontWeightnullExprRef",
|
||||
"ConditionalPredicateValueDefGradientstringnullExprRef",
|
||||
"ConditionalPredicateValueDefTextBaselinenullExprRef",
|
||||
"ConditionalPredicateValueDefTextExprRef",
|
||||
"ConditionalPredicateValueDefnumber",
|
||||
"ConditionalPredicateValueDefnumberArrayExprRef",
|
||||
"ConditionalPredicateValueDefnumberArraynullExprRef",
|
||||
"ConditionalPredicateValueDefnumberExprRef",
|
||||
"ConditionalPredicateValueDefnumbernullExprRef",
|
||||
"ConditionalPredicateValueDefstringExprRef",
|
||||
"ConditionalPredicateValueDefstringnullExprRef",
|
||||
"ConditionalStringFieldDef",
|
||||
"ConditionalValueDefGradientstringnullExprRef",
|
||||
"ConditionalValueDefTextExprRef",
|
||||
"ConditionalValueDefnumber",
|
||||
"ConditionalValueDefnumberArrayExprRef",
|
||||
"ConditionalValueDefnumberExprRef",
|
||||
"ConditionalValueDefstringExprRef",
|
||||
"ConditionalValueDefstringnullExprRef",
|
||||
"Config",
|
||||
"CsvDataFormat",
|
||||
"Cursor",
|
||||
"Cyclical",
|
||||
"Data",
|
||||
"DataFormat",
|
||||
"DataSource",
|
||||
"DataType",
|
||||
"Datasets",
|
||||
"DateTime",
|
||||
"DatumChannelMixin",
|
||||
"DatumDef",
|
||||
"Day",
|
||||
"DensityTransform",
|
||||
"DerivedStream",
|
||||
"Description",
|
||||
"DescriptionValue",
|
||||
"Detail",
|
||||
"DictInlineDataset",
|
||||
"DictSelectionInit",
|
||||
"DictSelectionInitInterval",
|
||||
"Diverging",
|
||||
"DomainUnionWith",
|
||||
"DsvDataFormat",
|
||||
"Element",
|
||||
"Encoding",
|
||||
"EncodingSortField",
|
||||
"ErrorBand",
|
||||
"ErrorBandConfig",
|
||||
"ErrorBandDef",
|
||||
"ErrorBar",
|
||||
"ErrorBarConfig",
|
||||
"ErrorBarDef",
|
||||
"ErrorBarExtent",
|
||||
"EventStream",
|
||||
"EventType",
|
||||
"Expr",
|
||||
"ExprRef",
|
||||
"ExtentTransform",
|
||||
"Facet",
|
||||
"FacetChart",
|
||||
"FacetEncodingFieldDef",
|
||||
"FacetFieldDef",
|
||||
"FacetMapping",
|
||||
"FacetSpec",
|
||||
"FacetedEncoding",
|
||||
"FacetedUnitSpec",
|
||||
"Feature",
|
||||
"FeatureCollection",
|
||||
"FeatureGeometryGeoJsonProperties",
|
||||
"Field",
|
||||
"FieldChannelMixin",
|
||||
"FieldDefWithoutScale",
|
||||
"FieldEqualPredicate",
|
||||
"FieldGTEPredicate",
|
||||
"FieldGTPredicate",
|
||||
"FieldLTEPredicate",
|
||||
"FieldLTPredicate",
|
||||
"FieldName",
|
||||
"FieldOneOfPredicate",
|
||||
"FieldOrDatumDefWithConditionDatumDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumber",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionDatumDefstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefTypeForShapestringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumber",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionStringDatumDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefstring",
|
||||
"FieldRange",
|
||||
"FieldRangePredicate",
|
||||
"FieldValidPredicate",
|
||||
"Fill",
|
||||
"FillDatum",
|
||||
"FillOpacity",
|
||||
"FillOpacityDatum",
|
||||
"FillOpacityValue",
|
||||
"FillValue",
|
||||
"FilterTransform",
|
||||
"Fit",
|
||||
"FlattenTransform",
|
||||
"FoldTransform",
|
||||
"FontStyle",
|
||||
"FontWeight",
|
||||
"FormatConfig",
|
||||
"Generator",
|
||||
"GenericUnitSpecEncodingAnyMark",
|
||||
"GeoJsonFeature",
|
||||
"GeoJsonFeatureCollection",
|
||||
"GeoJsonProperties",
|
||||
"Geometry",
|
||||
"GeometryCollection",
|
||||
"Gradient",
|
||||
"GradientStop",
|
||||
"GraticuleGenerator",
|
||||
"GraticuleParams",
|
||||
"HConcatChart",
|
||||
"HConcatSpecGenericSpec",
|
||||
"Header",
|
||||
"HeaderConfig",
|
||||
"HexColor",
|
||||
"Href",
|
||||
"HrefValue",
|
||||
"Impute",
|
||||
"ImputeMethod",
|
||||
"ImputeParams",
|
||||
"ImputeSequence",
|
||||
"ImputeTransform",
|
||||
"InlineData",
|
||||
"InlineDataset",
|
||||
"Interpolate",
|
||||
"IntervalSelectionConfig",
|
||||
"IntervalSelectionConfigWithoutType",
|
||||
"JoinAggregateFieldDef",
|
||||
"JoinAggregateTransform",
|
||||
"JsonDataFormat",
|
||||
"Key",
|
||||
"LabelOverlap",
|
||||
"LatLongDef",
|
||||
"LatLongFieldDef",
|
||||
"Latitude",
|
||||
"Latitude2",
|
||||
"Latitude2Datum",
|
||||
"Latitude2Value",
|
||||
"LatitudeDatum",
|
||||
"LayerChart",
|
||||
"LayerRepeatMapping",
|
||||
"LayerRepeatSpec",
|
||||
"LayerSpec",
|
||||
"LayoutAlign",
|
||||
"Legend",
|
||||
"LegendBinding",
|
||||
"LegendConfig",
|
||||
"LegendOrient",
|
||||
"LegendResolveMap",
|
||||
"LegendStreamBinding",
|
||||
"LineConfig",
|
||||
"LineString",
|
||||
"LinearGradient",
|
||||
"LocalMultiTimeUnit",
|
||||
"LocalSingleTimeUnit",
|
||||
"Locale",
|
||||
"LoessTransform",
|
||||
"LogicalAndPredicate",
|
||||
"LogicalNotPredicate",
|
||||
"LogicalOrPredicate",
|
||||
"Longitude",
|
||||
"Longitude2",
|
||||
"Longitude2Datum",
|
||||
"Longitude2Value",
|
||||
"LongitudeDatum",
|
||||
"LookupData",
|
||||
"LookupSelection",
|
||||
"LookupTransform",
|
||||
"Mark",
|
||||
"MarkConfig",
|
||||
"MarkDef",
|
||||
"MarkInvalidDataMode",
|
||||
"MarkPropDefGradientstringnull",
|
||||
"MarkPropDefnumber",
|
||||
"MarkPropDefnumberArray",
|
||||
"MarkPropDefstringnullTypeForShape",
|
||||
"MarkType",
|
||||
"MaxRowsError",
|
||||
"MergedStream",
|
||||
"Month",
|
||||
"MultiLineString",
|
||||
"MultiPoint",
|
||||
"MultiPolygon",
|
||||
"MultiTimeUnit",
|
||||
"NamedData",
|
||||
"NonArgAggregateOp",
|
||||
"NonLayerRepeatSpec",
|
||||
"NonNormalizedSpec",
|
||||
"NumberLocale",
|
||||
"NumericArrayMarkPropDef",
|
||||
"NumericMarkPropDef",
|
||||
"OffsetDef",
|
||||
"Opacity",
|
||||
"OpacityDatum",
|
||||
"OpacityValue",
|
||||
"Order",
|
||||
"OrderFieldDef",
|
||||
"OrderOnlyDef",
|
||||
"OrderValue",
|
||||
"OrderValueDef",
|
||||
"Orient",
|
||||
"Orientation",
|
||||
"OverlayMarkDef",
|
||||
"Padding",
|
||||
"Parameter",
|
||||
"ParameterExpression",
|
||||
"ParameterExtent",
|
||||
"ParameterName",
|
||||
"ParameterPredicate",
|
||||
"Parse",
|
||||
"ParseValue",
|
||||
"PivotTransform",
|
||||
"Point",
|
||||
"PointSelectionConfig",
|
||||
"PointSelectionConfigWithoutType",
|
||||
"PolarDef",
|
||||
"Polygon",
|
||||
"Position",
|
||||
"Position2Def",
|
||||
"PositionDatumDef",
|
||||
"PositionDatumDefBase",
|
||||
"PositionDef",
|
||||
"PositionFieldDef",
|
||||
"PositionFieldDefBase",
|
||||
"PositionValueDef",
|
||||
"Predicate",
|
||||
"PredicateComposition",
|
||||
"PrimitiveValue",
|
||||
"Projection",
|
||||
"ProjectionConfig",
|
||||
"ProjectionType",
|
||||
"QuantileTransform",
|
||||
"RadialGradient",
|
||||
"Radius",
|
||||
"Radius2",
|
||||
"Radius2Datum",
|
||||
"Radius2Value",
|
||||
"RadiusDatum",
|
||||
"RadiusValue",
|
||||
"RangeConfig",
|
||||
"RangeEnum",
|
||||
"RangeRaw",
|
||||
"RangeRawArray",
|
||||
"RangeScheme",
|
||||
"RectConfig",
|
||||
"RegressionTransform",
|
||||
"RelativeBandSize",
|
||||
"RepeatChart",
|
||||
"RepeatMapping",
|
||||
"RepeatRef",
|
||||
"RepeatSpec",
|
||||
"Resolve",
|
||||
"ResolveMode",
|
||||
"Root",
|
||||
"Row",
|
||||
"RowColLayoutAlign",
|
||||
"RowColboolean",
|
||||
"RowColnumber",
|
||||
"RowColumnEncodingFieldDef",
|
||||
"SampleTransform",
|
||||
"Scale",
|
||||
"ScaleBinParams",
|
||||
"ScaleBins",
|
||||
"ScaleConfig",
|
||||
"ScaleDatumDef",
|
||||
"ScaleFieldDef",
|
||||
"ScaleInterpolateEnum",
|
||||
"ScaleInterpolateParams",
|
||||
"ScaleInvalidDataConfig",
|
||||
"ScaleInvalidDataShowAsValueangle",
|
||||
"ScaleInvalidDataShowAsValuecolor",
|
||||
"ScaleInvalidDataShowAsValuefill",
|
||||
"ScaleInvalidDataShowAsValuefillOpacity",
|
||||
"ScaleInvalidDataShowAsValueopacity",
|
||||
"ScaleInvalidDataShowAsValueradius",
|
||||
"ScaleInvalidDataShowAsValueshape",
|
||||
"ScaleInvalidDataShowAsValuesize",
|
||||
"ScaleInvalidDataShowAsValuestroke",
|
||||
"ScaleInvalidDataShowAsValuestrokeDash",
|
||||
"ScaleInvalidDataShowAsValuestrokeOpacity",
|
||||
"ScaleInvalidDataShowAsValuestrokeWidth",
|
||||
"ScaleInvalidDataShowAsValuetheta",
|
||||
"ScaleInvalidDataShowAsValuex",
|
||||
"ScaleInvalidDataShowAsValuexOffset",
|
||||
"ScaleInvalidDataShowAsValuey",
|
||||
"ScaleInvalidDataShowAsValueyOffset",
|
||||
"ScaleInvalidDataShowAsangle",
|
||||
"ScaleInvalidDataShowAscolor",
|
||||
"ScaleInvalidDataShowAsfill",
|
||||
"ScaleInvalidDataShowAsfillOpacity",
|
||||
"ScaleInvalidDataShowAsopacity",
|
||||
"ScaleInvalidDataShowAsradius",
|
||||
"ScaleInvalidDataShowAsshape",
|
||||
"ScaleInvalidDataShowAssize",
|
||||
"ScaleInvalidDataShowAsstroke",
|
||||
"ScaleInvalidDataShowAsstrokeDash",
|
||||
"ScaleInvalidDataShowAsstrokeOpacity",
|
||||
"ScaleInvalidDataShowAsstrokeWidth",
|
||||
"ScaleInvalidDataShowAstheta",
|
||||
"ScaleInvalidDataShowAsx",
|
||||
"ScaleInvalidDataShowAsxOffset",
|
||||
"ScaleInvalidDataShowAsy",
|
||||
"ScaleInvalidDataShowAsyOffset",
|
||||
"ScaleResolveMap",
|
||||
"ScaleType",
|
||||
"SchemaBase",
|
||||
"SchemeParams",
|
||||
"SecondaryFieldDef",
|
||||
"SelectionConfig",
|
||||
"SelectionExpression",
|
||||
"SelectionInit",
|
||||
"SelectionInitInterval",
|
||||
"SelectionInitIntervalMapping",
|
||||
"SelectionInitMapping",
|
||||
"SelectionParameter",
|
||||
"SelectionPredicateComposition",
|
||||
"SelectionResolution",
|
||||
"SelectionType",
|
||||
"SequenceGenerator",
|
||||
"SequenceParams",
|
||||
"SequentialMultiHue",
|
||||
"SequentialSingleHue",
|
||||
"Shape",
|
||||
"ShapeDatum",
|
||||
"ShapeDef",
|
||||
"ShapeValue",
|
||||
"SharedEncoding",
|
||||
"SingleDefUnitChannel",
|
||||
"SingleTimeUnit",
|
||||
"Size",
|
||||
"SizeDatum",
|
||||
"SizeValue",
|
||||
"Sort",
|
||||
"SortArray",
|
||||
"SortByChannel",
|
||||
"SortByChannelDesc",
|
||||
"SortByEncoding",
|
||||
"SortField",
|
||||
"SortOrder",
|
||||
"Spec",
|
||||
"SphereGenerator",
|
||||
"StackOffset",
|
||||
"StackTransform",
|
||||
"StandardType",
|
||||
"Step",
|
||||
"StepFor",
|
||||
"Stream",
|
||||
"StringFieldDef",
|
||||
"StringFieldDefWithCondition",
|
||||
"StringValueDefWithCondition",
|
||||
"Stroke",
|
||||
"StrokeCap",
|
||||
"StrokeDash",
|
||||
"StrokeDashDatum",
|
||||
"StrokeDashValue",
|
||||
"StrokeDatum",
|
||||
"StrokeJoin",
|
||||
"StrokeOpacity",
|
||||
"StrokeOpacityDatum",
|
||||
"StrokeOpacityValue",
|
||||
"StrokeValue",
|
||||
"StrokeWidth",
|
||||
"StrokeWidthDatum",
|
||||
"StrokeWidthValue",
|
||||
"StyleConfigIndex",
|
||||
"SymbolShape",
|
||||
"Text",
|
||||
"TextBaseline",
|
||||
"TextDatum",
|
||||
"TextDef",
|
||||
"TextDirection",
|
||||
"TextValue",
|
||||
"Then",
|
||||
"Theta",
|
||||
"Theta2",
|
||||
"Theta2Datum",
|
||||
"Theta2Value",
|
||||
"ThetaDatum",
|
||||
"ThetaValue",
|
||||
"TickConfig",
|
||||
"TickCount",
|
||||
"TimeInterval",
|
||||
"TimeIntervalStep",
|
||||
"TimeLocale",
|
||||
"TimeUnit",
|
||||
"TimeUnitParams",
|
||||
"TimeUnitTransform",
|
||||
"TimeUnitTransformParams",
|
||||
"Title",
|
||||
"TitleAnchor",
|
||||
"TitleConfig",
|
||||
"TitleFrame",
|
||||
"TitleOrient",
|
||||
"TitleParams",
|
||||
"Tooltip",
|
||||
"TooltipContent",
|
||||
"TooltipValue",
|
||||
"TopLevelConcatSpec",
|
||||
"TopLevelFacetSpec",
|
||||
"TopLevelHConcatSpec",
|
||||
"TopLevelLayerSpec",
|
||||
"TopLevelMixin",
|
||||
"TopLevelParameter",
|
||||
"TopLevelRepeatSpec",
|
||||
"TopLevelSelectionParameter",
|
||||
"TopLevelSpec",
|
||||
"TopLevelUnitSpec",
|
||||
"TopLevelVConcatSpec",
|
||||
"TopoDataFormat",
|
||||
"Transform",
|
||||
"Type",
|
||||
"TypeForShape",
|
||||
"TypedFieldDef",
|
||||
"UnitSpec",
|
||||
"UnitSpecWithFrame",
|
||||
"Url",
|
||||
"UrlData",
|
||||
"UrlValue",
|
||||
"UtcMultiTimeUnit",
|
||||
"UtcSingleTimeUnit",
|
||||
"VConcatChart",
|
||||
"VConcatSpecGenericSpec",
|
||||
"ValueChannelMixin",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefGradientstringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefTypeForShapestringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumber",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumberArray",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefstringnull",
|
||||
"ValueDefWithConditionStringFieldDefText",
|
||||
"ValueDefnumber",
|
||||
"ValueDefnumberwidthheightExprRef",
|
||||
"VariableParameter",
|
||||
"Vector2DateTime",
|
||||
"Vector2Vector2number",
|
||||
"Vector2boolean",
|
||||
"Vector2number",
|
||||
"Vector2string",
|
||||
"Vector3number",
|
||||
"Vector7string",
|
||||
"Vector10string",
|
||||
"Vector12string",
|
||||
"VegaLite",
|
||||
"VegaLiteSchema",
|
||||
"ViewBackground",
|
||||
"ViewConfig",
|
||||
"When",
|
||||
"WindowEventType",
|
||||
"WindowFieldDef",
|
||||
"WindowOnlyOp",
|
||||
"WindowTransform",
|
||||
"X",
|
||||
"X2Datum",
|
||||
"X2Value",
|
||||
"XDatum",
|
||||
"XError",
|
||||
"XError2",
|
||||
"XError2Value",
|
||||
"XErrorValue",
|
||||
"XOffset",
|
||||
"XOffsetDatum",
|
||||
"XOffsetValue",
|
||||
"XValue",
|
||||
"Y",
|
||||
"Y2Datum",
|
||||
"Y2Value",
|
||||
"YDatum",
|
||||
"YError",
|
||||
"YError2",
|
||||
"YError2Value",
|
||||
"YErrorValue",
|
||||
"YOffset",
|
||||
"YOffsetDatum",
|
||||
"YOffsetValue",
|
||||
"YValue",
|
||||
"api",
|
||||
"binding",
|
||||
"binding_checkbox",
|
||||
"binding_radio",
|
||||
"binding_range",
|
||||
"binding_select",
|
||||
"channels",
|
||||
"check_fields_and_encodings",
|
||||
"compiler",
|
||||
"concat",
|
||||
"condition",
|
||||
"core",
|
||||
"data_transformers",
|
||||
"datum",
|
||||
"default_data_transformer",
|
||||
"graticule",
|
||||
"hconcat",
|
||||
"layer",
|
||||
"limit_rows",
|
||||
"load_schema",
|
||||
"mixins",
|
||||
"param",
|
||||
"renderers",
|
||||
"repeat",
|
||||
"sample",
|
||||
"schema",
|
||||
"selection",
|
||||
"selection_interval",
|
||||
"selection_multi",
|
||||
"selection_point",
|
||||
"selection_single",
|
||||
"sequence",
|
||||
"sphere",
|
||||
"to_csv",
|
||||
"to_json",
|
||||
"to_values",
|
||||
"topo_feature",
|
||||
"value",
|
||||
"vconcat",
|
||||
"vegalite_compilers",
|
||||
"when",
|
||||
"with_property_setters",
|
||||
]
|
||||
5213
myenv/lib/python3.11/site-packages/altair/vegalite/v5/api.py
Normal file
5213
myenv/lib/python3.11/site-packages/altair/vegalite/v5/api.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
from typing import Final
|
||||
|
||||
from altair.utils._importers import import_vl_convert
|
||||
from altair.utils.compiler import VegaLiteCompilerRegistry
|
||||
|
||||
ENTRY_POINT_GROUP: Final = "altair.vegalite.v5.vegalite_compiler"
|
||||
vegalite_compilers = VegaLiteCompilerRegistry(entry_point_group=ENTRY_POINT_GROUP)
|
||||
|
||||
|
||||
def vl_convert_compiler(vegalite_spec: dict) -> dict:
|
||||
"""Vega-Lite to Vega compiler that uses vl-convert."""
|
||||
from . import SCHEMA_VERSION
|
||||
|
||||
vlc = import_vl_convert()
|
||||
|
||||
# Compute vl-convert's vl_version string (of the form 'v5_8')
|
||||
# from SCHEMA_VERSION (of the form 'v5.8.0')
|
||||
vl_version = "_".join(SCHEMA_VERSION.split(".")[:2])
|
||||
return vlc.vegalite_to_vega(vegalite_spec, vl_version=vl_version)
|
||||
|
||||
|
||||
vegalite_compilers.register("vl-convert", vl_convert_compiler)
|
||||
vegalite_compilers.enable("vl-convert")
|
||||
@@ -0,0 +1,41 @@
|
||||
from typing import Final
|
||||
|
||||
from altair.utils._vegafusion_data import vegafusion_data_transformer
|
||||
from altair.vegalite.data import (
|
||||
DataTransformerRegistry,
|
||||
MaxRowsError,
|
||||
default_data_transformer,
|
||||
limit_rows,
|
||||
sample,
|
||||
to_csv,
|
||||
to_json,
|
||||
to_values,
|
||||
)
|
||||
|
||||
# ==============================================================================
|
||||
# VegaLite 5 data transformers
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
ENTRY_POINT_GROUP: Final = "altair.vegalite.v5.data_transformer"
|
||||
|
||||
|
||||
data_transformers = DataTransformerRegistry(entry_point_group=ENTRY_POINT_GROUP)
|
||||
data_transformers.register("default", default_data_transformer)
|
||||
data_transformers.register("json", to_json)
|
||||
# FIXME: `to_csv` cannot accept all `DataType` https://github.com/vega/altair/issues/3441
|
||||
data_transformers.register("csv", to_csv) # type: ignore[arg-type]
|
||||
data_transformers.register("vegafusion", vegafusion_data_transformer)
|
||||
data_transformers.enable("default")
|
||||
|
||||
|
||||
__all__ = (
|
||||
"MaxRowsError",
|
||||
"default_data_transformer",
|
||||
"limit_rows",
|
||||
"sample",
|
||||
"to_csv",
|
||||
"to_json",
|
||||
"to_values",
|
||||
"vegafusion_data_transformer",
|
||||
)
|
||||
191
myenv/lib/python3.11/site-packages/altair/vegalite/v5/display.py
Normal file
191
myenv/lib/python3.11/site-packages/altair/vegalite/v5/display.py
Normal file
@@ -0,0 +1,191 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from altair.utils.mimebundle import spec_to_mimebundle
|
||||
from altair.vegalite.display import (
|
||||
Displayable,
|
||||
HTMLRenderer,
|
||||
RendererRegistry,
|
||||
default_renderer_base,
|
||||
json_renderer_base,
|
||||
)
|
||||
|
||||
from .schema import SCHEMA_VERSION
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from altair.vegalite.display import DefaultRendererReturnType
|
||||
|
||||
|
||||
VEGALITE_VERSION: Final = SCHEMA_VERSION.lstrip("v")
|
||||
VEGA_VERSION: Final = "5"
|
||||
VEGAEMBED_VERSION: Final = "6"
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# VegaLite v5 renderer logic
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
# The MIME type for Vega-Lite 5.x releases.
|
||||
VEGALITE_MIME_TYPE: Final = "application/vnd.vegalite.v5+json"
|
||||
|
||||
# The MIME type for Vega 5.x releases.
|
||||
VEGA_MIME_TYPE: Final = "application/vnd.vega.v5+json"
|
||||
|
||||
# The entry point group that can be used by other packages to declare other
|
||||
# renderers that will be auto-detected. Explicit registration is also
|
||||
# allowed by the PluginRegistery API.
|
||||
ENTRY_POINT_GROUP: Final = "altair.vegalite.v5.renderer"
|
||||
|
||||
# The display message when rendering fails
|
||||
DEFAULT_DISPLAY: Final = f"""\
|
||||
<VegaLite {VEGALITE_VERSION.split('.')[0]} object>
|
||||
|
||||
If you see this message, it means the renderer has not been properly enabled
|
||||
for the frontend that you are using. For more information, see
|
||||
https://altair-viz.github.io/user_guide/display_frontends.html#troubleshooting
|
||||
"""
|
||||
|
||||
renderers = RendererRegistry(entry_point_group=ENTRY_POINT_GROUP)
|
||||
|
||||
here = str(Path(__file__).parent)
|
||||
|
||||
|
||||
def mimetype_renderer(spec: dict, **metadata) -> DefaultRendererReturnType:
|
||||
return default_renderer_base(spec, VEGALITE_MIME_TYPE, DEFAULT_DISPLAY, **metadata)
|
||||
|
||||
|
||||
def json_renderer(spec: dict, **metadata) -> DefaultRendererReturnType:
|
||||
return json_renderer_base(spec, DEFAULT_DISPLAY, **metadata)
|
||||
|
||||
|
||||
def png_renderer(spec: dict, **metadata) -> dict[str, bytes]:
|
||||
# To get proper return value type, would need to write complex
|
||||
# overload signatures for spec_to_mimebundle based on `format`
|
||||
return spec_to_mimebundle( # type: ignore[return-value]
|
||||
spec,
|
||||
format="png",
|
||||
mode="vega-lite",
|
||||
vega_version=VEGA_VERSION,
|
||||
vegaembed_version=VEGAEMBED_VERSION,
|
||||
vegalite_version=VEGALITE_VERSION,
|
||||
**metadata,
|
||||
)
|
||||
|
||||
|
||||
def svg_renderer(spec: dict, **metadata) -> dict[str, str]:
|
||||
# To get proper return value type, would need to write complex
|
||||
# overload signatures for spec_to_mimebundle based on `format`
|
||||
return spec_to_mimebundle(
|
||||
spec,
|
||||
format="svg",
|
||||
mode="vega-lite",
|
||||
vega_version=VEGA_VERSION,
|
||||
vegaembed_version=VEGAEMBED_VERSION,
|
||||
vegalite_version=VEGALITE_VERSION,
|
||||
**metadata,
|
||||
)
|
||||
|
||||
|
||||
def jupyter_renderer(spec: dict, **metadata):
|
||||
"""Render chart using the JupyterChart Jupyter Widget."""
|
||||
from altair import Chart, JupyterChart
|
||||
|
||||
# Configure offline mode
|
||||
offline = metadata.get("offline", False)
|
||||
|
||||
# mypy doesn't see the enable_offline class method for some reason
|
||||
JupyterChart.enable_offline(offline=offline) # type: ignore[attr-defined]
|
||||
|
||||
# propagate embed options
|
||||
embed_options = metadata.get("embed_options")
|
||||
|
||||
# Need to ignore attr-defined mypy rule because mypy doesn't see _repr_mimebundle_
|
||||
# conditionally defined in AnyWidget
|
||||
return JupyterChart(
|
||||
chart=Chart.from_dict(spec), embed_options=embed_options
|
||||
)._repr_mimebundle_() # type: ignore[attr-defined]
|
||||
|
||||
|
||||
def browser_renderer(
|
||||
spec: dict, offline=False, using=None, port=0, **metadata
|
||||
) -> dict[str, str]:
|
||||
from altair.utils._show import open_html_in_browser
|
||||
|
||||
if offline:
|
||||
metadata["template"] = "inline"
|
||||
mimebundle = spec_to_mimebundle(
|
||||
spec,
|
||||
format="html",
|
||||
mode="vega-lite",
|
||||
vega_version=VEGA_VERSION,
|
||||
vegaembed_version=VEGAEMBED_VERSION,
|
||||
vegalite_version=VEGALITE_VERSION,
|
||||
**metadata,
|
||||
)
|
||||
html = mimebundle["text/html"]
|
||||
open_html_in_browser(html, using=using, port=port)
|
||||
return {}
|
||||
|
||||
|
||||
html_renderer = HTMLRenderer(
|
||||
mode="vega-lite",
|
||||
template="universal",
|
||||
vega_version=VEGA_VERSION,
|
||||
vegaembed_version=VEGAEMBED_VERSION,
|
||||
vegalite_version=VEGALITE_VERSION,
|
||||
)
|
||||
|
||||
|
||||
olli_renderer = HTMLRenderer(
|
||||
mode="vega-lite",
|
||||
template="olli",
|
||||
vega_version=VEGA_VERSION,
|
||||
vegaembed_version=VEGAEMBED_VERSION,
|
||||
vegalite_version=VEGALITE_VERSION,
|
||||
)
|
||||
|
||||
renderers.register("default", html_renderer)
|
||||
renderers.register("html", html_renderer)
|
||||
renderers.register("colab", html_renderer)
|
||||
renderers.register("kaggle", html_renderer)
|
||||
renderers.register("zeppelin", html_renderer)
|
||||
renderers.register("mimetype", mimetype_renderer)
|
||||
renderers.register("jupyterlab", mimetype_renderer)
|
||||
renderers.register("nteract", mimetype_renderer)
|
||||
renderers.register("json", json_renderer)
|
||||
renderers.register("png", png_renderer)
|
||||
renderers.register("svg", svg_renderer)
|
||||
# FIXME: Caused by upstream # type: ignore[unreachable]
|
||||
# https://github.com/manzt/anywidget/blob/b7961305a7304f4d3def1fafef0df65db56cf41e/anywidget/widget.py#L80-L81
|
||||
renderers.register("jupyter", jupyter_renderer) # pyright: ignore[reportArgumentType]
|
||||
renderers.register("browser", browser_renderer)
|
||||
renderers.register("olli", olli_renderer)
|
||||
renderers.enable("default")
|
||||
|
||||
|
||||
class VegaLite(Displayable):
|
||||
"""An IPython/Jupyter display class for rendering VegaLite 5."""
|
||||
|
||||
renderers = renderers
|
||||
schema_path = (__name__, "schema/vega-lite-schema.json")
|
||||
|
||||
|
||||
def vegalite(spec: dict, validate: bool = True) -> None:
|
||||
"""
|
||||
Render and optionally validate a VegaLite 5 spec.
|
||||
|
||||
This will use the currently enabled renderer to render the spec.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spec: dict
|
||||
A fully compliant VegaLite 5 spec, with the data portion fully processed.
|
||||
validate: bool
|
||||
Should the spec be validated against the VegaLite 5 schema?
|
||||
"""
|
||||
from IPython.display import display
|
||||
|
||||
display(VegaLite(spec, validate=validate))
|
||||
@@ -0,0 +1,571 @@
|
||||
# ruff: noqa: F403, F405
|
||||
# The contents of this file are automatically written by
|
||||
# tools/generate_schema_wrapper.py. Do not modify directly.
|
||||
|
||||
from altair.vegalite.v5.schema import channels, core
|
||||
from altair.vegalite.v5.schema.channels import *
|
||||
from altair.vegalite.v5.schema.core import *
|
||||
|
||||
SCHEMA_VERSION = "v5.20.1"
|
||||
|
||||
SCHEMA_URL = "https://vega.github.io/schema/vega-lite/v5.20.1.json"
|
||||
|
||||
__all__ = [
|
||||
"SCHEMA_URL",
|
||||
"SCHEMA_VERSION",
|
||||
"URI",
|
||||
"X2",
|
||||
"Y2",
|
||||
"Aggregate",
|
||||
"AggregateOp",
|
||||
"AggregateTransform",
|
||||
"AggregatedFieldDef",
|
||||
"Align",
|
||||
"AllSortString",
|
||||
"Angle",
|
||||
"AngleDatum",
|
||||
"AngleValue",
|
||||
"AnyMark",
|
||||
"AnyMarkConfig",
|
||||
"AreaConfig",
|
||||
"ArgmaxDef",
|
||||
"ArgminDef",
|
||||
"AutoSizeParams",
|
||||
"AutosizeType",
|
||||
"Axis",
|
||||
"AxisConfig",
|
||||
"AxisOrient",
|
||||
"AxisResolveMap",
|
||||
"BBox",
|
||||
"BarConfig",
|
||||
"BaseTitleNoValueRefs",
|
||||
"Baseline",
|
||||
"BinExtent",
|
||||
"BinParams",
|
||||
"BinTransform",
|
||||
"BindCheckbox",
|
||||
"BindDirect",
|
||||
"BindInput",
|
||||
"BindRadioSelect",
|
||||
"BindRange",
|
||||
"Binding",
|
||||
"BinnedTimeUnit",
|
||||
"Blend",
|
||||
"BoxPlot",
|
||||
"BoxPlotConfig",
|
||||
"BoxPlotDef",
|
||||
"BrushConfig",
|
||||
"CalculateTransform",
|
||||
"Categorical",
|
||||
"Color",
|
||||
"ColorDatum",
|
||||
"ColorDef",
|
||||
"ColorName",
|
||||
"ColorScheme",
|
||||
"ColorValue",
|
||||
"Column",
|
||||
"CompositeMark",
|
||||
"CompositeMarkDef",
|
||||
"CompositionConfig",
|
||||
"ConcatSpecGenericSpec",
|
||||
"ConditionalAxisColor",
|
||||
"ConditionalAxisLabelAlign",
|
||||
"ConditionalAxisLabelBaseline",
|
||||
"ConditionalAxisLabelFontStyle",
|
||||
"ConditionalAxisLabelFontWeight",
|
||||
"ConditionalAxisNumber",
|
||||
"ConditionalAxisNumberArray",
|
||||
"ConditionalAxisPropertyAlignnull",
|
||||
"ConditionalAxisPropertyColornull",
|
||||
"ConditionalAxisPropertyFontStylenull",
|
||||
"ConditionalAxisPropertyFontWeightnull",
|
||||
"ConditionalAxisPropertyTextBaselinenull",
|
||||
"ConditionalAxisPropertynumberArraynull",
|
||||
"ConditionalAxisPropertynumbernull",
|
||||
"ConditionalAxisPropertystringnull",
|
||||
"ConditionalAxisString",
|
||||
"ConditionalMarkPropFieldOrDatumDef",
|
||||
"ConditionalMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDef",
|
||||
"ConditionalParameterMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalParameterStringFieldDef",
|
||||
"ConditionalParameterValueDefGradientstringnullExprRef",
|
||||
"ConditionalParameterValueDefTextExprRef",
|
||||
"ConditionalParameterValueDefnumber",
|
||||
"ConditionalParameterValueDefnumberArrayExprRef",
|
||||
"ConditionalParameterValueDefnumberExprRef",
|
||||
"ConditionalParameterValueDefstringExprRef",
|
||||
"ConditionalParameterValueDefstringnullExprRef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDef",
|
||||
"ConditionalPredicateMarkPropFieldOrDatumDefTypeForShape",
|
||||
"ConditionalPredicateStringFieldDef",
|
||||
"ConditionalPredicateValueDefAlignnullExprRef",
|
||||
"ConditionalPredicateValueDefColornullExprRef",
|
||||
"ConditionalPredicateValueDefFontStylenullExprRef",
|
||||
"ConditionalPredicateValueDefFontWeightnullExprRef",
|
||||
"ConditionalPredicateValueDefGradientstringnullExprRef",
|
||||
"ConditionalPredicateValueDefTextBaselinenullExprRef",
|
||||
"ConditionalPredicateValueDefTextExprRef",
|
||||
"ConditionalPredicateValueDefnumber",
|
||||
"ConditionalPredicateValueDefnumberArrayExprRef",
|
||||
"ConditionalPredicateValueDefnumberArraynullExprRef",
|
||||
"ConditionalPredicateValueDefnumberExprRef",
|
||||
"ConditionalPredicateValueDefnumbernullExprRef",
|
||||
"ConditionalPredicateValueDefstringExprRef",
|
||||
"ConditionalPredicateValueDefstringnullExprRef",
|
||||
"ConditionalStringFieldDef",
|
||||
"ConditionalValueDefGradientstringnullExprRef",
|
||||
"ConditionalValueDefTextExprRef",
|
||||
"ConditionalValueDefnumber",
|
||||
"ConditionalValueDefnumberArrayExprRef",
|
||||
"ConditionalValueDefnumberExprRef",
|
||||
"ConditionalValueDefstringExprRef",
|
||||
"ConditionalValueDefstringnullExprRef",
|
||||
"Config",
|
||||
"CsvDataFormat",
|
||||
"Cursor",
|
||||
"Cyclical",
|
||||
"Data",
|
||||
"DataFormat",
|
||||
"DataSource",
|
||||
"Datasets",
|
||||
"DateTime",
|
||||
"DatumChannelMixin",
|
||||
"DatumDef",
|
||||
"Day",
|
||||
"DensityTransform",
|
||||
"DerivedStream",
|
||||
"Description",
|
||||
"DescriptionValue",
|
||||
"Detail",
|
||||
"DictInlineDataset",
|
||||
"DictSelectionInit",
|
||||
"DictSelectionInitInterval",
|
||||
"Diverging",
|
||||
"DomainUnionWith",
|
||||
"DsvDataFormat",
|
||||
"Element",
|
||||
"Encoding",
|
||||
"EncodingSortField",
|
||||
"ErrorBand",
|
||||
"ErrorBandConfig",
|
||||
"ErrorBandDef",
|
||||
"ErrorBar",
|
||||
"ErrorBarConfig",
|
||||
"ErrorBarDef",
|
||||
"ErrorBarExtent",
|
||||
"EventStream",
|
||||
"EventType",
|
||||
"Expr",
|
||||
"ExprRef",
|
||||
"ExtentTransform",
|
||||
"Facet",
|
||||
"FacetEncodingFieldDef",
|
||||
"FacetFieldDef",
|
||||
"FacetSpec",
|
||||
"FacetedEncoding",
|
||||
"FacetedUnitSpec",
|
||||
"Feature",
|
||||
"FeatureCollection",
|
||||
"FeatureGeometryGeoJsonProperties",
|
||||
"Field",
|
||||
"FieldChannelMixin",
|
||||
"FieldDefWithoutScale",
|
||||
"FieldEqualPredicate",
|
||||
"FieldGTEPredicate",
|
||||
"FieldGTPredicate",
|
||||
"FieldLTEPredicate",
|
||||
"FieldLTPredicate",
|
||||
"FieldName",
|
||||
"FieldOneOfPredicate",
|
||||
"FieldOrDatumDefWithConditionDatumDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumber",
|
||||
"FieldOrDatumDefWithConditionDatumDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionDatumDefstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefGradientstringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefTypeForShapestringnull",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumber",
|
||||
"FieldOrDatumDefWithConditionMarkPropFieldDefnumberArray",
|
||||
"FieldOrDatumDefWithConditionStringDatumDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefText",
|
||||
"FieldOrDatumDefWithConditionStringFieldDefstring",
|
||||
"FieldRange",
|
||||
"FieldRangePredicate",
|
||||
"FieldValidPredicate",
|
||||
"Fill",
|
||||
"FillDatum",
|
||||
"FillOpacity",
|
||||
"FillOpacityDatum",
|
||||
"FillOpacityValue",
|
||||
"FillValue",
|
||||
"FilterTransform",
|
||||
"Fit",
|
||||
"FlattenTransform",
|
||||
"FoldTransform",
|
||||
"FontStyle",
|
||||
"FontWeight",
|
||||
"FormatConfig",
|
||||
"Generator",
|
||||
"GenericUnitSpecEncodingAnyMark",
|
||||
"GeoJsonFeature",
|
||||
"GeoJsonFeatureCollection",
|
||||
"GeoJsonProperties",
|
||||
"Geometry",
|
||||
"GeometryCollection",
|
||||
"Gradient",
|
||||
"GradientStop",
|
||||
"GraticuleGenerator",
|
||||
"GraticuleParams",
|
||||
"HConcatSpecGenericSpec",
|
||||
"Header",
|
||||
"HeaderConfig",
|
||||
"HexColor",
|
||||
"Href",
|
||||
"HrefValue",
|
||||
"ImputeMethod",
|
||||
"ImputeParams",
|
||||
"ImputeSequence",
|
||||
"ImputeTransform",
|
||||
"InlineData",
|
||||
"InlineDataset",
|
||||
"Interpolate",
|
||||
"IntervalSelectionConfig",
|
||||
"IntervalSelectionConfigWithoutType",
|
||||
"JoinAggregateFieldDef",
|
||||
"JoinAggregateTransform",
|
||||
"JsonDataFormat",
|
||||
"Key",
|
||||
"LabelOverlap",
|
||||
"LatLongDef",
|
||||
"LatLongFieldDef",
|
||||
"Latitude",
|
||||
"Latitude2",
|
||||
"Latitude2Datum",
|
||||
"Latitude2Value",
|
||||
"LatitudeDatum",
|
||||
"LayerRepeatMapping",
|
||||
"LayerRepeatSpec",
|
||||
"LayerSpec",
|
||||
"LayoutAlign",
|
||||
"Legend",
|
||||
"LegendBinding",
|
||||
"LegendConfig",
|
||||
"LegendOrient",
|
||||
"LegendResolveMap",
|
||||
"LegendStreamBinding",
|
||||
"LineConfig",
|
||||
"LineString",
|
||||
"LinearGradient",
|
||||
"LocalMultiTimeUnit",
|
||||
"LocalSingleTimeUnit",
|
||||
"Locale",
|
||||
"LoessTransform",
|
||||
"LogicalAndPredicate",
|
||||
"LogicalNotPredicate",
|
||||
"LogicalOrPredicate",
|
||||
"Longitude",
|
||||
"Longitude2",
|
||||
"Longitude2Datum",
|
||||
"Longitude2Value",
|
||||
"LongitudeDatum",
|
||||
"LookupSelection",
|
||||
"LookupTransform",
|
||||
"Mark",
|
||||
"MarkConfig",
|
||||
"MarkDef",
|
||||
"MarkInvalidDataMode",
|
||||
"MarkPropDefGradientstringnull",
|
||||
"MarkPropDefnumber",
|
||||
"MarkPropDefnumberArray",
|
||||
"MarkPropDefstringnullTypeForShape",
|
||||
"MarkType",
|
||||
"MergedStream",
|
||||
"Month",
|
||||
"MultiLineString",
|
||||
"MultiPoint",
|
||||
"MultiPolygon",
|
||||
"MultiTimeUnit",
|
||||
"NamedData",
|
||||
"NonArgAggregateOp",
|
||||
"NonLayerRepeatSpec",
|
||||
"NonNormalizedSpec",
|
||||
"NumberLocale",
|
||||
"NumericArrayMarkPropDef",
|
||||
"NumericMarkPropDef",
|
||||
"OffsetDef",
|
||||
"Opacity",
|
||||
"OpacityDatum",
|
||||
"OpacityValue",
|
||||
"Order",
|
||||
"OrderFieldDef",
|
||||
"OrderOnlyDef",
|
||||
"OrderValue",
|
||||
"OrderValueDef",
|
||||
"Orient",
|
||||
"Orientation",
|
||||
"OverlayMarkDef",
|
||||
"Padding",
|
||||
"ParameterExtent",
|
||||
"ParameterName",
|
||||
"ParameterPredicate",
|
||||
"Parse",
|
||||
"ParseValue",
|
||||
"PivotTransform",
|
||||
"Point",
|
||||
"PointSelectionConfig",
|
||||
"PointSelectionConfigWithoutType",
|
||||
"PolarDef",
|
||||
"Polygon",
|
||||
"Position",
|
||||
"Position2Def",
|
||||
"PositionDatumDef",
|
||||
"PositionDatumDefBase",
|
||||
"PositionDef",
|
||||
"PositionFieldDef",
|
||||
"PositionFieldDefBase",
|
||||
"PositionValueDef",
|
||||
"Predicate",
|
||||
"PredicateComposition",
|
||||
"PrimitiveValue",
|
||||
"Projection",
|
||||
"ProjectionConfig",
|
||||
"ProjectionType",
|
||||
"QuantileTransform",
|
||||
"RadialGradient",
|
||||
"Radius",
|
||||
"Radius2",
|
||||
"Radius2Datum",
|
||||
"Radius2Value",
|
||||
"RadiusDatum",
|
||||
"RadiusValue",
|
||||
"RangeConfig",
|
||||
"RangeEnum",
|
||||
"RangeRaw",
|
||||
"RangeRawArray",
|
||||
"RangeScheme",
|
||||
"RectConfig",
|
||||
"RegressionTransform",
|
||||
"RelativeBandSize",
|
||||
"RepeatMapping",
|
||||
"RepeatRef",
|
||||
"RepeatSpec",
|
||||
"Resolve",
|
||||
"ResolveMode",
|
||||
"Root",
|
||||
"Row",
|
||||
"RowColLayoutAlign",
|
||||
"RowColboolean",
|
||||
"RowColnumber",
|
||||
"RowColumnEncodingFieldDef",
|
||||
"SampleTransform",
|
||||
"Scale",
|
||||
"ScaleBinParams",
|
||||
"ScaleBins",
|
||||
"ScaleConfig",
|
||||
"ScaleDatumDef",
|
||||
"ScaleFieldDef",
|
||||
"ScaleInterpolateEnum",
|
||||
"ScaleInterpolateParams",
|
||||
"ScaleInvalidDataConfig",
|
||||
"ScaleInvalidDataShowAsValueangle",
|
||||
"ScaleInvalidDataShowAsValuecolor",
|
||||
"ScaleInvalidDataShowAsValuefill",
|
||||
"ScaleInvalidDataShowAsValuefillOpacity",
|
||||
"ScaleInvalidDataShowAsValueopacity",
|
||||
"ScaleInvalidDataShowAsValueradius",
|
||||
"ScaleInvalidDataShowAsValueshape",
|
||||
"ScaleInvalidDataShowAsValuesize",
|
||||
"ScaleInvalidDataShowAsValuestroke",
|
||||
"ScaleInvalidDataShowAsValuestrokeDash",
|
||||
"ScaleInvalidDataShowAsValuestrokeOpacity",
|
||||
"ScaleInvalidDataShowAsValuestrokeWidth",
|
||||
"ScaleInvalidDataShowAsValuetheta",
|
||||
"ScaleInvalidDataShowAsValuex",
|
||||
"ScaleInvalidDataShowAsValuexOffset",
|
||||
"ScaleInvalidDataShowAsValuey",
|
||||
"ScaleInvalidDataShowAsValueyOffset",
|
||||
"ScaleInvalidDataShowAsangle",
|
||||
"ScaleInvalidDataShowAscolor",
|
||||
"ScaleInvalidDataShowAsfill",
|
||||
"ScaleInvalidDataShowAsfillOpacity",
|
||||
"ScaleInvalidDataShowAsopacity",
|
||||
"ScaleInvalidDataShowAsradius",
|
||||
"ScaleInvalidDataShowAsshape",
|
||||
"ScaleInvalidDataShowAssize",
|
||||
"ScaleInvalidDataShowAsstroke",
|
||||
"ScaleInvalidDataShowAsstrokeDash",
|
||||
"ScaleInvalidDataShowAsstrokeOpacity",
|
||||
"ScaleInvalidDataShowAsstrokeWidth",
|
||||
"ScaleInvalidDataShowAstheta",
|
||||
"ScaleInvalidDataShowAsx",
|
||||
"ScaleInvalidDataShowAsxOffset",
|
||||
"ScaleInvalidDataShowAsy",
|
||||
"ScaleInvalidDataShowAsyOffset",
|
||||
"ScaleResolveMap",
|
||||
"ScaleType",
|
||||
"SchemaBase",
|
||||
"SchemeParams",
|
||||
"SecondaryFieldDef",
|
||||
"SelectionConfig",
|
||||
"SelectionInit",
|
||||
"SelectionInitInterval",
|
||||
"SelectionInitIntervalMapping",
|
||||
"SelectionInitMapping",
|
||||
"SelectionParameter",
|
||||
"SelectionResolution",
|
||||
"SelectionType",
|
||||
"SequenceGenerator",
|
||||
"SequenceParams",
|
||||
"SequentialMultiHue",
|
||||
"SequentialSingleHue",
|
||||
"Shape",
|
||||
"ShapeDatum",
|
||||
"ShapeDef",
|
||||
"ShapeValue",
|
||||
"SharedEncoding",
|
||||
"SingleDefUnitChannel",
|
||||
"SingleTimeUnit",
|
||||
"Size",
|
||||
"SizeDatum",
|
||||
"SizeValue",
|
||||
"Sort",
|
||||
"SortArray",
|
||||
"SortByChannel",
|
||||
"SortByChannelDesc",
|
||||
"SortByEncoding",
|
||||
"SortField",
|
||||
"SortOrder",
|
||||
"Spec",
|
||||
"SphereGenerator",
|
||||
"StackOffset",
|
||||
"StackTransform",
|
||||
"StandardType",
|
||||
"Step",
|
||||
"StepFor",
|
||||
"Stream",
|
||||
"StringFieldDef",
|
||||
"StringFieldDefWithCondition",
|
||||
"StringValueDefWithCondition",
|
||||
"Stroke",
|
||||
"StrokeCap",
|
||||
"StrokeDash",
|
||||
"StrokeDashDatum",
|
||||
"StrokeDashValue",
|
||||
"StrokeDatum",
|
||||
"StrokeJoin",
|
||||
"StrokeOpacity",
|
||||
"StrokeOpacityDatum",
|
||||
"StrokeOpacityValue",
|
||||
"StrokeValue",
|
||||
"StrokeWidth",
|
||||
"StrokeWidthDatum",
|
||||
"StrokeWidthValue",
|
||||
"StyleConfigIndex",
|
||||
"SymbolShape",
|
||||
"Text",
|
||||
"TextBaseline",
|
||||
"TextDatum",
|
||||
"TextDef",
|
||||
"TextDirection",
|
||||
"TextValue",
|
||||
"Theta",
|
||||
"Theta2",
|
||||
"Theta2Datum",
|
||||
"Theta2Value",
|
||||
"ThetaDatum",
|
||||
"ThetaValue",
|
||||
"TickConfig",
|
||||
"TickCount",
|
||||
"TimeInterval",
|
||||
"TimeIntervalStep",
|
||||
"TimeLocale",
|
||||
"TimeUnit",
|
||||
"TimeUnitParams",
|
||||
"TimeUnitTransform",
|
||||
"TimeUnitTransformParams",
|
||||
"TitleAnchor",
|
||||
"TitleConfig",
|
||||
"TitleFrame",
|
||||
"TitleOrient",
|
||||
"TitleParams",
|
||||
"Tooltip",
|
||||
"TooltipContent",
|
||||
"TooltipValue",
|
||||
"TopLevelConcatSpec",
|
||||
"TopLevelFacetSpec",
|
||||
"TopLevelHConcatSpec",
|
||||
"TopLevelLayerSpec",
|
||||
"TopLevelParameter",
|
||||
"TopLevelRepeatSpec",
|
||||
"TopLevelSelectionParameter",
|
||||
"TopLevelSpec",
|
||||
"TopLevelUnitSpec",
|
||||
"TopLevelVConcatSpec",
|
||||
"TopoDataFormat",
|
||||
"Transform",
|
||||
"Type",
|
||||
"TypeForShape",
|
||||
"TypedFieldDef",
|
||||
"UnitSpec",
|
||||
"UnitSpecWithFrame",
|
||||
"Url",
|
||||
"UrlData",
|
||||
"UrlValue",
|
||||
"UtcMultiTimeUnit",
|
||||
"UtcSingleTimeUnit",
|
||||
"VConcatSpecGenericSpec",
|
||||
"ValueChannelMixin",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefGradientstringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefTypeForShapestringnull",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumber",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefnumberArray",
|
||||
"ValueDefWithConditionMarkPropFieldOrDatumDefstringnull",
|
||||
"ValueDefWithConditionStringFieldDefText",
|
||||
"ValueDefnumber",
|
||||
"ValueDefnumberwidthheightExprRef",
|
||||
"VariableParameter",
|
||||
"Vector2DateTime",
|
||||
"Vector2Vector2number",
|
||||
"Vector2boolean",
|
||||
"Vector2number",
|
||||
"Vector2string",
|
||||
"Vector3number",
|
||||
"Vector7string",
|
||||
"Vector10string",
|
||||
"Vector12string",
|
||||
"VegaLiteSchema",
|
||||
"ViewBackground",
|
||||
"ViewConfig",
|
||||
"WindowEventType",
|
||||
"WindowFieldDef",
|
||||
"WindowOnlyOp",
|
||||
"WindowTransform",
|
||||
"X",
|
||||
"X2Datum",
|
||||
"X2Value",
|
||||
"XDatum",
|
||||
"XError",
|
||||
"XError2",
|
||||
"XError2Value",
|
||||
"XErrorValue",
|
||||
"XOffset",
|
||||
"XOffsetDatum",
|
||||
"XOffsetValue",
|
||||
"XValue",
|
||||
"Y",
|
||||
"Y2Datum",
|
||||
"Y2Value",
|
||||
"YDatum",
|
||||
"YError",
|
||||
"YError2",
|
||||
"YError2Value",
|
||||
"YErrorValue",
|
||||
"YOffset",
|
||||
"YOffsetDatum",
|
||||
"YOffsetValue",
|
||||
"YValue",
|
||||
"channels",
|
||||
"core",
|
||||
"load_schema",
|
||||
"with_property_setters",
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
22040
myenv/lib/python3.11/site-packages/altair/vegalite/v5/schema/channels.py
Normal file
22040
myenv/lib/python3.11/site-packages/altair/vegalite/v5/schema/channels.py
Normal file
File diff suppressed because it is too large
Load Diff
27199
myenv/lib/python3.11/site-packages/altair/vegalite/v5/schema/core.py
Normal file
27199
myenv/lib/python3.11/site-packages/altair/vegalite/v5/schema/core.py
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
125
myenv/lib/python3.11/site-packages/altair/vegalite/v5/theme.py
Normal file
125
myenv/lib/python3.11/site-packages/altair/vegalite/v5/theme.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Tools for enabling and registering chart themes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal, get_args
|
||||
|
||||
from altair.utils.deprecation import deprecated_static_only
|
||||
from altair.utils.plugin_registry import Plugin, PluginRegistry
|
||||
from altair.vegalite.v5.schema._config import ThemeConfig
|
||||
from altair.vegalite.v5.schema._typing import VegaThemes
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import LiteralString
|
||||
else:
|
||||
from typing_extensions import LiteralString
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from altair.utils.plugin_registry import PluginEnabler
|
||||
|
||||
|
||||
AltairThemes: TypeAlias = Literal["default", "opaque"]
|
||||
VEGA_THEMES: list[LiteralString] = list(get_args(VegaThemes))
|
||||
|
||||
|
||||
# HACK: See for `LiteralString` requirement in `name`
|
||||
# https://github.com/vega/altair/pull/3526#discussion_r1743350127
|
||||
class ThemeRegistry(PluginRegistry[Plugin[ThemeConfig], ThemeConfig]):
|
||||
def enable(
|
||||
self,
|
||||
name: LiteralString | AltairThemes | VegaThemes | None = None,
|
||||
**options: Any,
|
||||
) -> PluginEnabler[Plugin[ThemeConfig], ThemeConfig]:
|
||||
"""
|
||||
Enable a theme by name.
|
||||
|
||||
This can be either called directly, or used as a context manager.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : string (optional)
|
||||
The name of the theme to enable. If not specified, then use the
|
||||
current active name.
|
||||
**options :
|
||||
Any additional parameters will be passed to the theme as keyword
|
||||
arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
PluginEnabler:
|
||||
An object that allows enable() to be used as a context manager
|
||||
|
||||
Notes
|
||||
-----
|
||||
Default `vega` themes can be previewed at https://vega.github.io/vega-themes/
|
||||
"""
|
||||
return super().enable(name, **options)
|
||||
|
||||
def get(self) -> partial[ThemeConfig] | Plugin[ThemeConfig] | None:
|
||||
"""Return the currently active theme."""
|
||||
return super().get()
|
||||
|
||||
def names(self) -> list[str]:
|
||||
"""Return the names of the registered and entry points themes."""
|
||||
return super().names()
|
||||
|
||||
@deprecated_static_only(
|
||||
"Deprecated since `altair=5.5.0`. Use @altair.theme.register instead.",
|
||||
category=None,
|
||||
)
|
||||
def register(
|
||||
self, name: str, value: Plugin[ThemeConfig] | None
|
||||
) -> Plugin[ThemeConfig] | None:
|
||||
return super().register(name, value)
|
||||
|
||||
|
||||
class VegaTheme:
|
||||
"""Implementation of a builtin vega theme."""
|
||||
|
||||
def __init__(self, theme: str) -> None:
|
||||
self.theme = theme
|
||||
|
||||
def __call__(self) -> ThemeConfig:
|
||||
return {
|
||||
"usermeta": {"embedOptions": {"theme": self.theme}},
|
||||
"config": {"view": {"continuousWidth": 300, "continuousHeight": 300}},
|
||||
}
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"VegaTheme({self.theme!r})"
|
||||
|
||||
|
||||
# The entry point group that can be used by other packages to declare other
|
||||
# themes that will be auto-detected. Explicit registration is also
|
||||
# allowed by the PluginRegistry API.
|
||||
ENTRY_POINT_GROUP: Final = "altair.vegalite.v5.theme"
|
||||
|
||||
# NOTE: `themes` def has an entry point group
|
||||
themes = ThemeRegistry(entry_point_group=ENTRY_POINT_GROUP)
|
||||
|
||||
themes.register(
|
||||
"default",
|
||||
lambda: {"config": {"view": {"continuousWidth": 300, "continuousHeight": 300}}},
|
||||
)
|
||||
themes.register(
|
||||
"opaque",
|
||||
lambda: {
|
||||
"config": {
|
||||
"background": "white",
|
||||
"view": {"continuousWidth": 300, "continuousHeight": 300},
|
||||
}
|
||||
},
|
||||
)
|
||||
themes.register("none", ThemeConfig)
|
||||
|
||||
for theme in VEGA_THEMES:
|
||||
themes.register(theme, VegaTheme(theme))
|
||||
|
||||
themes.enable("default")
|
||||
Reference in New Issue
Block a user