Skip to content

Commit

Permalink
Add ObservationAnalyzer plugin
Browse files Browse the repository at this point in the history
- Add observation_response_controller
- Add misfits properties to realization
- Add misfits dataframe creation to response
- Update observation_response_controller
- Black ert-style.json; add observation-response-plot styles
- Fix for checking if schema is None or empty
- Add option to change yaxis scale
- Add BoxPlotModel into plots and integrate with observation_response_controller
  • Loading branch information
xjules committed Dec 14, 2020
1 parent ee6da48 commit a3e6b2c
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 24 deletions.
72 changes: 52 additions & 20 deletions ertviz/assets/ert-style.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"ensemble-selector": {
"stylesheet": [
{
"selector" : "edge",
"selector": "edge",
"style": {
"line-color" : "rgba(128,128,128,0.5)"
"line-color": "rgba(128,128,128,0.5)"
}
},
{
"selector" : "node",
"selector": "node",
"style": {
"label": "data(label)"
}
Expand All @@ -29,33 +29,65 @@
"rgba(191,91,23,0.8)"
]
},
"response-plot":{
"observation-response-plot": {
"observation": {
"mode" : "markers",
"color" : "rgb(176, 28, 52)",
"mode": "markers",
"color": "rgb(176, 28, 52)",
"line": null,
"marker" : {
"marker": {
"symbol": "line-ew-open",
"color": "rgb(176, 28, 52)",
"size" : 10
"size": 20,
"line": {
"width": 5,
"color": "DarkSlateGrey"
}
}
},
"misfits": {
"mode": "markers+lines",
"line": {
"color": "rgba(56,108,176,0.8)",
"dash": "dash",
"width": 2
},
"marker": {
"color": "rgba(56,108,176,0.8)",
"size": 10,
"line": {
"width": 3,
"color": "DarkSlateGrey"
}
}
}
},
"response-plot": {
"observation": {
"mode": "markers",
"color": "rgb(176, 28, 52)",
"line": null,
"marker": {
"color": "rgb(176, 28, 52)",
"size": 10
}
},
"response" : {
"mode" : "markers+lines",
"response": {
"mode": "markers+lines",
"line": {
"color" : "rgba(56,108,176,0.8)"
"color": "rgba(56,108,176,0.8)"
},
"marker" : {
"color" : "rgba(56,108,176,0.8)",
"size" : 1
"marker": {
"color": "rgba(56,108,176,0.8)",
"size": 1
}
},
"statistics" : {
"mode" : "lines",
"statistics": {
"mode": "lines",
"line": {
"color" : "rgba(56,108,176,0.8)",
"dash" : "dash"
"color": "rgba(56,108,176,0.8)",
"dash": "dash"
},
"marker" : null
"marker": null
}
},
"figure":{
Expand All @@ -66,4 +98,4 @@
"autosize": true
}
}
}
}
4 changes: 4 additions & 0 deletions ertviz/assets/webviz-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ pages:
- title: Response Comparison Viewer
content:
- ResponseComparison:

- title: Observation Analyzer
content:
- ObservationAnalyzer:
1 change: 1 addition & 0 deletions ertviz/controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ def parse_url_query(search):
from .link_and_brush_controller import link_and_brush_controller
from .ensemble_selector_controller import ensemble_selector_controller
from .multi_response_controller import multi_response_controller
from .observation_response_controller import observation_response_controller
from .multi_parameter_controller import multi_parameter_controller
154 changes: 154 additions & 0 deletions ertviz/controllers/observation_response_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from copy import deepcopy
from ertviz.models import ResponsePlotModel, BoxPlotModel, PlotModel, EnsembleModel
import ertviz.assets as assets


def _get_univariate_misfits_plots(misfits_df, color):
if misfits_df is None:
return []
style = deepcopy(assets.ERTSTYLE["observation-response-plot"]["misfits"])
style["line"]["color"] = color
style["marker"]["color"] = color
x_axis = misfits_df.pop("x_axis")
misfits_data = list()
for misfits in misfits_df:
plot = PlotModel(
x_axis=x_axis,
y_axis=misfits_df[misfits].pow(1 / 2.0).values,
text=misfits,
name=misfits,
**style,
)
misfits_data.append(plot)
return misfits_data


def _get_univariate_misfits_boxplots(misfits_df, color):
if misfits_df is None:
return []
x_axis = misfits_df.pop("x_axis")
misfits_data = list()
for misfits in misfits_df.T:
plot = BoxPlotModel(
x_axis=[x_axis.loc[misfits]],
y_axis=misfits_df.T[misfits].pow(1 / 2.0).values,
text=misfits,
name=f"Misfits@{int(x_axis.loc[misfits])}",
color=color,
)
misfits_data.append(plot)
return misfits_data


def _get_observation_plots(observation_df, x_axis):
data = observation_df["values"]
stds = observation_df["std"]
x_axis = observation_df["x_axis"]
style = deepcopy(assets.ERTSTYLE["observation-response-plot"]["observation"])
observation_data = PlotModel(
x_axis=x_axis,
y_axis=stds.values,
text="Observations",
name="Observations",
**style,
)
return [observation_data]


def _create_misfits_plot(response, yaxis_type, selected_realizations, color):

x_axis = response.axis
# realizations = _get_univariate_misfits_plots(
realizations = _get_univariate_misfits_boxplots(
response.univariate_misfits_df(selected_realizations), color=color
)
observations = []

# for obs in response.observations:
# observations += _get_observation_plots(obs.data_df(), x_axis)

ensemble_plot = ResponsePlotModel(
realizations,
observations,
dict(
xaxis={"title": "Index"},
yaxis={"title": "Unit TODO"},
hovermode="closest",
uirevision=True,
),
)
return ensemble_plot


def observation_response_controller(parent, app):
@app.callback(
Output(parent.uuid("response-selector"), "options"),
[Input(parent.uuid("ensemble-selection-store"), "data")],
)
def _set_response_options(selected_ensembles):
# Should either return a union of all possible responses or the other thing which I cant think of...

if not selected_ensembles:
raise PreventUpdate
ensemble_id, _ = selected_ensembles.popitem()
ensemble = parent.ensembles.get(ensemble_id, EnsembleModel(ref_url=ensemble_id))
parent.ensembles[ensemble_id] = ensemble
return [
{
"label": response,
"value": response,
}
for response in ensemble.responses
]

@app.callback(
Output(parent.uuid("response-selector"), "value"),
[Input(parent.uuid("response-selector"), "options")],
[State(parent.uuid("response-selector"), "value")],
)
def _set_responses_value(available_options, previous_selected_response):
if available_options and previous_selected_response in [
opt["value"] for opt in available_options
]:
return previous_selected_response
if available_options and not previous_selected_response:
return available_options[0]["value"]
return ""

@app.callback(
Output(
{"id": parent.uuid("response-graphic"), "type": parent.uuid("graph")},
"figure",
),
[
Input(parent.uuid("response-selector"), "value"),
Input(parent.uuid("yaxis-type"), "value"),
Input(parent.uuid("misfits-type"), "value"),
],
[State(parent.uuid("ensemble-selection-store"), "data")],
)
def _update_graph(response, yaxis_type, misfits_type, selected_ensembles):
if response in [None, ""] or not selected_ensembles:
raise PreventUpdate

def _generate_plot(ensemble_id, color):
ensemble = parent.ensembles.get(ensemble_id, None)
plot = _create_misfits_plot(
ensemble.responses[response], yaxis_type, None, color
)
return plot

response_plots = [
_generate_plot(ensemble_id, data["color"])
for ensemble_id, data in selected_ensembles.items()
]

fig = go.Figure()
for plot in response_plots:
for trace in plot.repr.data:
fig.add_trace(trace)
fig.update_yaxes(type=yaxis_type)
return fig
1 change: 1 addition & 0 deletions ertviz/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def indexes_to_axis(indexes):
ResponsePlotModel,
HistogramPlotModel,
MultiHistogramPlotModel,
BoxPlotModel,
)
from .parameter_model import PriorModel, ParametersModel
from .ensemble_model import EnsembleModel
35 changes: 35 additions & 0 deletions ertviz/models/plot_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@
import plotly.figure_factory as ff


class BoxPlotModel:
def __init__(self, **kwargs):
self.selected = True
self._x_axis = kwargs["x_axis"]
self._y_axis = kwargs["y_axis"]
self._text = kwargs["text"]
self._name = kwargs["name"]
self._color = kwargs["color"]

@property
def repr(self):
repr_dict = dict(
# x=self._x_axis,
y=self._y_axis,
# text=self.display_name,
name=self.display_name,
boxpoints="all",
jitter=0.3,
pointpos=-1.8,
marker_color=self._color,
)
return go.Box(repr_dict)

@property
def name(self):
return self._name

@property
def display_name(self):
if isinstance(self._name, int):
return f"Value {self._name}"
else:
return self._name


class PlotModel:
def __init__(self, **kwargs):
self._x_axis = kwargs["x_axis"]
Expand Down
42 changes: 40 additions & 2 deletions ertviz/models/realization.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
import pandas as pd
from ertviz.data_loader import get_data


class Realization:
def __init__(self, realization_schema):
self.name = realization_schema["name"]
self.data = realization_schema["data"]
self._name = realization_schema["name"]
self._data = realization_schema["data"]
self._univariate_misfits_df = self._extract_univariate_misfits(
realization_schema.get("univariate_misfits")
)
self._summarized_missfits_value = self._extract_summary_misfits(
realization_schema.get("summarized_misfits")
)

def _extract_univariate_misfits(self, schema):
if bool(schema): # this account for not None and empty dict
misfits_ = list(schema.values())
return pd.DataFrame(misfits_[0])
return None

def _extract_summary_misfits(self, schema):
if bool(schema):
misfits_ = list(schema.values())[0]
return misfits_
return None

@property
def summarized_misfits_value(self):
return self._summarized_missfits_value

@property
def univariate_misfits_df(self):
return self._univariate_misfits_df

@property
def data(self):
return self._data

@property
def name(self):
return self._name
Loading

0 comments on commit a3e6b2c

Please sign in to comment.