{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Specifying Intent in Lux" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lux provides a flexible language for communicating your analysis intent to the system, so that Lux can provide better and more relevant recommendations to you. In this tutorial, we will see different ways of specifying the intent, including the attributes and values that you are interested or not interested in, enumeration specifiers, as well as any constraints on the visualization encoding.\n", "\n", "The primary way to set the current intent associated with a dataframe is by setting the `intent` property of the dataframe, and providing a list of specification as input. We will first describe how intent can be specified through convenient shorthand descriptions as string inputs, then we will describe advance usage via the `lux.Clause` object.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic descriptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Continuing with our college dataset example from earlier," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import lux" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv(\"../data/college.csv\")\n", "lux.config.default_display = \"lux\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Specifying attributes of interest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can indicate that you are interested in an attribute, let's say `AverageCost`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost']\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might be interested in multiple attributes, for instance you might want to look at both `AverageCost` and `FundingModel`. When multiple clauses are specified, Lux applies all the clauses in the intent and searches for visualizations that are relevant to `AverageCost` **and** `FundingModel`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost','FundingModel']\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's say that in addition to `AverageCost`, you are interested in the looking at a list of attributes that are related to different financial measures, such as `Expenditure` or `MedianDebt`, and how they breakdown with respect to `FundingModel`. \n", "\n", "You can specify a list of desired attributes separated by the `|` symbol, which indicates an `OR` relationship between the list of attributes. If multiple clauses are specified, Lux automatically create combinations of the specified attributes. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "possible_attributes = \"AverageCost|Expenditure|MedianDebt|MedianEarnings\"\n", "df.intent = [possible_attributes,\"FundingModel\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you could also provide the specification as a list: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "possible_attributes = ['AverageCost','Expenditure','MedianDebt','MedianEarnings']\n", "df.intent = [possible_attributes,\"FundingModel\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Specifying values of interest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In Lux, you can also specify particular values corresponding to subsets of the data that you might be interested in. For example, you may be interested in only colleges located in New England. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [\"Region=New England\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also specify multiple values of interest using the same `|` notation that we saw earlier. For example, you can compare the median debt of students from colleges in New England, Southeast, and Far West." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [\"MedianDebt\",\"Region=New England|Southeast|Far West\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that since there are three different visualizations that is generated based on the intent, we only display these possible visualization, rather than the recommendations. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.clear_intent()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Note: Applying Filters v.s. Expressing Filter Intent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might be wondering what is the difference between specifying values of interest through the intent in Lux versus applying a filter directly on the dataframe through Pandas. By specifying the intent directly via Pandas, Lux is not aware of the specified inputs to Pandas, so these values of interest will not be reflected in the recommendations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df[df[\"Region\"]==\"New England\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Specifying the values through `set_intent` tells Lux that you are interested in colleges in New England. In the resulting Filter action, we see that Lux suggests visualizations in other `Region`s as recommendations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [\"Region=New England\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So while both approaches applies the filter on the specified visualization, the subtle difference between *applying* a filter and *indicating* a filter intent leads to different sets of resulting recommendations. In general, we encourage using Pandas for filtering if you are certain about applying the filter (e.g., a cleaning operation deleting a specific data subset), and specify the intent through Lux if you might want to experiment and change aspects related to the filter in your analysis. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Advanced intent specification through `lux.Clause`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The basic string-based description provides a convenient way of specifying the intent. However, not all specification can be expressed through the string-based descriptions, more complex specification can be expressed through the `lux.Clause` object. The two modes of specification is essentially equivalent, with the Parser parsing the `description` field in the `lux.Clause` object." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Specifying attributes or values of interest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To see an example of how lux.Clause is used, we rewrite our earlier example of expressing interest in `AverageCost` as: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [lux.Clause(attribute='AverageCost')]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Similarly, we can use `lux.Clause` to specify values of interest:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['MedianDebt',\n", " lux.Clause(attribute='Region',filter_op='=', value=['New England','Southeast','Far West'])]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both the `attribute` and `value` fields can take in either a single string or a list of attributes to specify items of interest. This example also demonstrates how we can intermix the `lux.Clause` specification alongside the basic string-based specification for convenience." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Adding constraints to override auto-inferred details" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So far, we have seen examples of how Lux takes in a loosely specified intent and automatically fills in many of the details that is required to generate the intended visualizations. There are situations where the user may want to override these auto-inferred values. For example, you might be interested in fixing an attribute to show up on a particular axis, ensuring that an aggregated attribute is summed up instead of averaged by default, or picking a specific bin size for a histogram. Additional properties specified on lux.Clause acts as constraints to the specified intent. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fixing attributes to specific axis channels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we saw earlier, when we set `AverageCost` as the intent, Lux generates a histogram with `AverageCost` on the x-axis.\n", "While this is unconventional, let's say that instead we want to set `AverageCost` to the y axis. We would specify this as additional properties to constrain the intent clause." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [lux.Clause(attribute='AverageCost', channel='y')]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Changing aggregation function applied" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also set constraints on the type of aggregation that is used. For example, by default, we use `mean` as the default aggregation function for quantitative attributes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [\"HighestDegree\",\"AverageCost\"]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can override the aggregation function to be `sum` instead. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = [\"HighestDegree\",lux.Clause(\"AverageCost\",aggregation=\"sum\")]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The possible aggregation values are the same as the ones supported in Pandas's [agg](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.agg.html) function, which can either be a string shorthand (e.g., \"sum\", \"count\", \"min\", \"max\", \"median\") or as a numpy aggregation function.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, we can change the aggregation function to be the point-to-point value ([np.ptp](https://numpy.org/doc/stable/reference/generated/numpy.ptp.html)) by inputting the numpy function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "df.intent = [\"HighestDegree\",lux.Clause(\"AverageCost\",aggregation=np.ptp)]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Specifying wildcards" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's say that you are interested in *any* attribute with respect to `AverageCost`. Lux support *wildcards* (based on [CompassQL](https://idl.cs.washington.edu/papers/compassql/) ), which specifies the enumeration of any possible attribute or values that satisfies the provided constraints." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost',lux.Clause('?')]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The space of enumeration can be narrowed based on constraints. For example, you might only be interested in looking at scatterplots of `AverageCost` with respect to quantitative attributes. This narrows the 15 visualizations that we had earlier to only 9 visualizations now, involving only quantitative attributes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost',lux.Clause('?',data_type='quantitative')]\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The enumeration specifier can also be placed on the value field. For example, you might be interested in looking at how the distribution of `AverageCost` varies for all possible values of `Geography`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost','Geography=?']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OR" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.intent = ['AverageCost',lux.Clause(attribute='Geography',filter_op='=',value='?')]\n", "df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.7" } }, "nbformat": 4, "nbformat_minor": 4 }