1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
"""
Utility function to load a variety of python objects into glue
"""
# Note: this is imported with Glue. We want
# to minimize imports so that utilities like glue-deps
# can run on systems with missing dependencies
import sys
from contextlib import contextmanager
import numpy as np
from glue.config import qglue_parser
try:
from glue.core import BaseData, Data
except ImportError:
# let qglue import, even though this won't work
# qglue will throw an ImportError
BaseData = Data = None
__all__ = ['qglue']
@contextmanager
def restore_io():
stdin = sys.stdin
stdout = sys.stdout
stderr = sys.stderr
_in = sys.__stdin__
_out = sys.__stdout__
_err = sys.__stderr__
try:
yield
finally:
sys.stdin = stdin
sys.stdout = stdout
sys.stderr = stderr
sys.__stdin__ = _in
sys.__stdout__ = _out
sys.__stderr__ = _err
@qglue_parser(dict)
def _parse_data_dict(data, label):
result = Data(label=label)
for label, component in data.items():
result.add_component(component, label)
return [result]
@qglue_parser(np.recarray)
def _parse_data_recarray(data, label):
kwargs = dict((n, data[n]) for n in data.dtype.names)
return [Data(label=label, **kwargs)]
@qglue_parser(BaseData)
def _parse_data_glue_data(data, label):
if isinstance(data, Data):
data.label = label
return [data]
@qglue_parser(np.ndarray)
def _parse_data_numpy(data, label):
return [Data(**{label: data, 'label': label})]
@qglue_parser(list)
def _parse_data_list(data, label):
return [Data(**{label: data, 'label': label})]
@qglue_parser(str)
def _parse_data_path(path, label):
from glue.core.data_factories import load_data, as_list
data = load_data(path)
for d in as_list(data):
d.label = label
return as_list(data)
def parse_data(data, label):
# First try new data translation layer
from glue.config import data_translator
try:
handler, preferred = data_translator.get_handler_for(data)
except TypeError:
pass
else:
data = handler.to_data(data)
data.label = label
data._preferred_translation = preferred
return [data]
# Then try legacy 'qglue_parser' infrastructure
for item in qglue_parser:
data_class = item.data_class
parser = item.parser
if isinstance(data, data_class):
try:
return parser(data, label)
except Exception as e:
raise ValueError("Invalid format for data '%s'\n\n%s" %
(label, e))
raise TypeError("Invalid data description: %s" % data)
def parse_links(dc, links):
from glue.core.link_helpers import MultiLink
from glue.core import ComponentLink
data = dict((d.label, d) for d in dc)
result = []
def find_cid(s):
dlabel, clabel = s.split('.')
d = data[dlabel]
c = d.find_component_id(clabel)
if c is None:
raise ValueError("Invalid link (no component named %s)" % s)
return c
for link in links:
f, t = link[0:2] # from and to component names
u = u2 = None
if len(link) >= 3: # forward translation function
u = link[2]
if len(link) == 4: # reverse translation function
u2 = link[3]
# component names -> component IDs
if isinstance(f, str):
f = [find_cid(f)]
else:
f = [find_cid(item) for item in f]
if isinstance(t, str):
t = find_cid(t)
result.append(ComponentLink(f, t, u))
else:
t = [find_cid(item) for item in t]
result += MultiLink(f, t, u, u2)
return result
def qglue(**kwargs):
"""
Quickly send python variables to Glue for visualization.
The generic calling sequence is::
qglue(label1=data1, label2=data2, ..., [links=links])
The kewyords label1, label2, ... can be named anything besides ``links``
data1, data2, ... can be in many formats:
* A pandas data frame
* A path to a file
* A numpy array, or python list
* A numpy rec array
* A dictionary of numpy arrays with the same shape
* An astropy Table
``Links`` is an optional list of link descriptions, each of which has
the format: ([left_ids], [right_ids], forward, backward)
Each ``left_id``/``right_id`` is a string naming a component in a dataset
(i.e., ``data1.x``). ``forward`` and ``backward`` are functions which
map quantities on the left to quantities on the right, and vice
versa. `backward` is optional
Examples::
balls = {'kg': [1, 2, 3], 'radius_cm': [10, 15, 30]}
cones = {'lbs': [5, 3, 3, 1]}
def lb2kg(lb):
return lb / 2.2
def kg2lb(kg):
return kg * 2.2
links = [(['balls.kg'], ['cones.lbs'], lb2kg, kg2lb)]
qglue(balls=balls, cones=cones, links=links)
:returns: A :class:`~glue.app.qt.application.GlueApplication` object
"""
from glue.core import DataCollection
from glue.app.qt import GlueApplication
from glue.dialogs.autolinker.qt import run_autolinker
links = kwargs.pop('links', None)
dc = DataCollection()
for label, data in kwargs.items():
dc.extend(parse_data(data, label))
if links is not None:
dc.add_link(parse_links(dc, links))
with restore_io():
ga = GlueApplication(dc)
run_autolinker(dc)
ga.start()
return ga
|