def retinotopy_comparison(arg1, arg2, arg3=None,<EOL>eccentricity_range=None, polar_angle_range=None, visual_area_mask=None,<EOL>weight=Ellipsis, weight_min=None, visual_area=Ellipsis,<EOL>method='<STR_LIT>', distance='<STR_LIT>', gold=None):
if arg3 is not None: (obj, dsets) = (arg1, [retinotopy_data(arg1, aa) for aa in (arg2,arg3)])<EOL>else:                (obj, dsets) = (None,    [arg1, arg2])<EOL>(gi,gold) = (None,False) if not gold else (gold-<NUM_LIT:1>,True)<EOL>result = {}<EOL>vis = [as_retinotopy(ds, '<STR_LIT>') for ds in dsets]<EOL>ps = (vis[<NUM_LIT:0>][<NUM_LIT:0>], vis[<NUM_LIT:1>][<NUM_LIT:0>])<EOL>es = (vis[<NUM_LIT:0>][<NUM_LIT:1>], vis[<NUM_LIT:1>][<NUM_LIT:1>])<EOL>rs = [ds['<STR_LIT>'] if '<STR_LIT>' in ds else None for ds in dsets]<EOL>for ii in (<NUM_LIT:0>,<NUM_LIT:1>):<EOL><INDENT>s = '<STR_LIT>' % (ii + <NUM_LIT:1>)<EOL>(p,e) = (ps[ii],es[ii])<EOL>result['<STR_LIT>'  + s] = p<EOL>result['<STR_LIT>' + s] = e<EOL>if rs[ii] is not None: result['<STR_LIT>' + s] = rs[ii]<EOL>p = np.pi/<NUM_LIT> * (<NUM_LIT> - p)<EOL>(x,y) = (e*np.cos(p), e*np.sin(p))<EOL>result['<STR_LIT:x>' + s] = x<EOL>result['<STR_LIT:y>' + s] = y<EOL>result['<STR_LIT:z>' + s] = x + <NUM_LIT> * y<EOL><DEDENT>n = len(ps[<NUM_LIT:0>])<EOL>if pimms.is_vector(weight) and len(weight) == <NUM_LIT:2>:<EOL><INDENT>ws = [(None  if w is None                   else<EOL>ds[w] if pimms.is_str(w) and w in ds else<EOL>geo.to_property(obj, w))<EOL>for (w,ds) in zip(weight, dsets)]<EOL>weight = Ellipsis<EOL><DEDENT>else:<EOL><INDENT>ws = [next((ds[k] for k in ('<STR_LIT>','<STR_LIT>') if k in ds), None)<EOL>for ds in dsets]<EOL><DEDENT>if pimms.is_vector(weight, '<STR_LIT>'):<EOL><INDENT>wgt = weight<EOL><DEDENT>elif pimms.is_str(weight):<EOL><INDENT>if obj is None: raise ValueError('<STR_LIT>')<EOL>wgt = geo.to_property(obj, weight)<EOL><DEDENT>elif weight is Ellipsis:<EOL><INDENT>if gold: wgt = ws[gi]<EOL>elif ws[<NUM_LIT:0>] is None and ws[<NUM_LIT:1>] is None: wgt = None<EOL>elif ws[<NUM_LIT:0>] is None: wgt = ws[<NUM_LIT:1>]<EOL>elif ws[<NUM_LIT:1>] is None: wgt = ws[<NUM_LIT:0>]<EOL>else: wgt = ws[<NUM_LIT:0>] * ws[<NUM_LIT:1>]<EOL><DEDENT>else: raise ValueError('<STR_LIT>')<EOL>if wgt is None: wgt = np.ones(n)<EOL>if ws[<NUM_LIT:0>] is not None: result['<STR_LIT>'] = ws[<NUM_LIT:0>]<EOL>if ws[<NUM_LIT:1>] is not None: result['<STR_LIT>'] = ws[<NUM_LIT:1>]<EOL>if is_tuple(visual_area) and len(visual_area) == <NUM_LIT:2>:<EOL><INDENT>ls = [(None  if l is None                   else<EOL>ds[l] if pimms.is_str(l) and l in ds else<EOL>geo.to_property(obj, l))<EOL>for (l,ds) in zip(visual_area, dsets)]<EOL>visual_area = Ellipsis<EOL><DEDENT>else:<EOL><INDENT>ls = [next((ds[k] for k in ('<STR_LIT>','<STR_LIT:label>') if k in ds), None)<EOL>for ds in dsets]<EOL><DEDENT>if pimms.is_vector(visual_area):<EOL><INDENT>lbl = visual_area<EOL><DEDENT>elif pimms.is_str(visual_area):<EOL><INDENT>if obj is None: raise ValueError('<STR_LIT>')<EOL>lbl = geo.to_property(obj, visual_area)<EOL><DEDENT>elif visual_area is None:<EOL><INDENT>lbl = None<EOL><DEDENT>elif visual_area is Ellipsis:<EOL><INDENT>if gold: lbl = ls[gi]<EOL>elif ls[<NUM_LIT:0>] is None and ls[<NUM_LIT:1>] is None: lbl = None<EOL>elif ls[<NUM_LIT:0>] is None: lbl = ls[<NUM_LIT:1>]<EOL>elif ls[<NUM_LIT:1>] is None: lbl = ls[<NUM_LIT:0>]<EOL>else: lbl = l[<NUM_LIT:0>] * (l[<NUM_LIT:0>] == l[<NUM_LIT:1>])<EOL><DEDENT>else: raise ValueError('<STR_LIT>')<EOL>if ls[<NUM_LIT:0>] is not None: result['<STR_LIT>'] = ls[<NUM_LIT:0>]<EOL>if ls[<NUM_LIT:1>] is not None: result['<STR_LIT>'] = ls[<NUM_LIT:1>]<EOL>wgt = np.array(wgt)<EOL>if weight_min is not None: wgt[wgt < weight_min] = <NUM_LIT:0><EOL>lbl = None if lbl is None else np.array(lbl)<EOL>if lbl is not None and visual_area_mask is not None:<EOL><INDENT>if pimms.is_int(visual_area_mask): visual_area_mask = [visual_area_mask]<EOL>oomask = (<NUM_LIT:0> == np.sum([lbl == va for va in visual_area_mask], axis=<NUM_LIT:0>))<EOL>wgt[oomask] = <NUM_LIT:0><EOL>lbl[oomask] = <NUM_LIT:0><EOL><DEDENT>if lbl is not None: result['<STR_LIT>'] = lbl<EOL>if eccentricity_range is not None:<EOL><INDENT>er = eccentricity_range<EOL>if pimms.is_real(er): er = (<NUM_LIT:0>,er)<EOL>if gold: wgt[(es[gi] < er[<NUM_LIT:0>]) | (es[gi] > er[<NUM_LIT:1>])] = <NUM_LIT:0><EOL>else:    wgt[(es[<NUM_LIT:0>] < er[<NUM_LIT:0>]) | (es[<NUM_LIT:0>] > er[<NUM_LIT:1>]) | (es[<NUM_LIT:1>] < er[<NUM_LIT:0>]) | (es[<NUM_LIT:1>] > er[<NUM_LIT:1>])] = <NUM_LIT:0><EOL><DEDENT>if polar_angle_range is not None:<EOL><INDENT>pr = polar_angle_range<EOL>if pimms.is_str(pr):<EOL><INDENT>pr = pr.lower()<EOL>if   pr in ['<STR_LIT>', '<STR_LIT>']: pr = (   <NUM_LIT:0>, <NUM_LIT>)<EOL>elif pr in ['<STR_LIT>', '<STR_LIT>']: pr = (-<NUM_LIT>,   <NUM_LIT:0>)<EOL>else: raise ValueError('<STR_LIT>' % pr)<EOL><DEDENT>if gold: wgt[(ps[gi] < pr[<NUM_LIT:0>]) | (ps[gi] > pr[<NUM_LIT:1>])] = <NUM_LIT:0><EOL>else:    wgt[(ps[<NUM_LIT:0>] < pr[<NUM_LIT:0>]) | (ps[<NUM_LIT:0>] > pr[<NUM_LIT:1>]) | (ps[<NUM_LIT:1>] < pr[<NUM_LIT:0>]) | (ps[<NUM_LIT:1>] > pr[<NUM_LIT:1>])] = <NUM_LIT:0><EOL><DEDENT>result['<STR_LIT>'] = wgt * zinv(np.sum(wgt))<EOL>gsecc = es[gi] if gold else np.mean(es, axis=<NUM_LIT:0>)<EOL>gsang = ps[gi] if gold else np.mean(ps, axis=<NUM_LIT:0>)<EOL>gsrad = rs[gi] if gold else rs[<NUM_LIT:0>] if rs[<NUM_LIT:1>] is None else rs[<NUM_LIT:1>] if rs[<NUM_LIT:0>] is None elsenp.mean(rs, axis=<NUM_LIT:0>)<EOL>gsecc_inv = zinv(gsecc)<EOL>gsrad_inv = None if gsrad is None else zinv(gsrad)<EOL>for (tag,resprop) in [('<STR_LIT:z>', '<STR_LIT>'), ('<STR_LIT>', '<STR_LIT>'),<EOL>('<STR_LIT>', '<STR_LIT>'), ('<STR_LIT:x>', '<STR_LIT:x>'), ('<STR_LIT:y>', '<STR_LIT:y>')]:<EOL><INDENT>serr = result[tag + '<STR_LIT>'] - result[tag + '<STR_LIT>']<EOL>aerr = np.abs(serr)<EOL>result[resprop + '<STR_LIT>'] = serr<EOL>result[resprop + '<STR_LIT>'] = aerr<EOL>result[resprop + '<STR_LIT>'] = aerr * gsecc_inv<EOL>if gsrad_inv is not None:<EOL><INDENT>result[resprop + '<STR_LIT>'] = aerr * gsrad_inv<EOL><DEDENT><DEDENT>return pimms.itable(result)<EOL>
retinotopy_comparison(dataset1, dataset2) yields a pimms itable comparing the two retinotopy
  datasets.
retinotopy_error(obj, dataset1, dataset2) is equivalent to retinotopy_comparison(x, y) where x
  and y are retinotopy(obj, dataset1) and retinotopy_data(obj, dataset2).

The datasets may be specified in a number of ways, some of which may be incompatible with
certain options. The simplest way to specify a dataset is as a vector of complex numbers, which
are taken to represent positions in the visual field with (a + bi) corresponding to the
coordinate (a deg, b deg) in the visual field. Alternately, an n x 2 or 2 x n matrix will be
interpreted as (polar angle, eccentricity) coordinates, in terms of visual degrees (see the
as_retinotopy function: as_retinotopy(arg, 'visual') yields this input format). Alternately,
the datasets may be mappings such as those retuend by the retinotopy_data function; in this case
as_retinotopy is used to extract the visual coordinates (so they need not be specified in visual
coordinates specifically in this case). In this last case, additional properties such as the
variance explained and pRF size can be returned, making it valuable for more sophisticated error
methods or distance metrics.

The returned dataset will always have a row for each row in the two datasets (which must have
the same number of rows). However, many rows may have a weight of 0 even if no weights were 
specified in the options; this is because other limitations may have been specified (such as
in the eccentricity_range or visual_areas). The returned dataset will always contain the
following columns:
  * 'weight' gives the weight assigned to this particular vertex; the weights will always sum to
    1 unless all vertices have 0 weight.
  * 'polar_angle_1' and 'polar_angle_2', 'eccentricity_1', 'eccenticity_2', 'x_1', 'x_2', 'y_1',
    'y_2', 'z_1', and 'z_2' all give the visual field coordinates in degrees; the z values give
    complex numbers equivalent to the x/y values.
  * 'radius_1' and 'radius_2' give the radii (sigma parameters) of the pRF gaussians.
  * 'polar_angle_error', 'eccentricity_error', and 'center_error' all give the difference
    between the visual field points in the two datasets; note that polar_angle_error in
    particular is an error measure of rotations around the visual field and not of visual field
    position. The 'center_error' is the distance between the centers of the visual field, in
    degrees. The 'radius_error' value is also given.
  * 'visual_area_1' and 'visual_area_2' specify the visual areas of the individual datasets; if
    either of the datasets did not have a visual area, it will be omitted. Additionally, the
    property 'visual_area' specifies the visual area suggested for use in analyses; this is
    chosen based on the following: (1) if there is a gold standard dataset specified that has
    a visual area, use it; (2) if only one of the datasets has a visual area, use it; (3) if
    both have a visual area, then use the (varea1 == varea2) * varea1 (the areas that agree are
    kept and all others are set to 0); (4) if neither has a visual area, then this property is
    omitted. In all cases where a 'visual_area' property is included, those vertices that do not
    overlap with the given visual_area_option option will be set to 0 along with the
    corresponding weights.
  * A variety of other lazily-calculated error metrics are included.

The following options are accepted:
  * eccentricity_range (default: None) specifies the range of eccentricity to include in the
    calculation (in degrees). This may be specified as emax or (emin, emax).
  * polar_angle_range (default: None) specifies the range of polar angles to include in the
    calculation. Like eccentricity range it may be specified as (amin, amax) but amax alone is
    not allowed. Additionally the strings 'lh' and 'rvf' are equivalent to (0,180) and the
    strings 'rh' and 'lvf' are equivalent to (-180,0).
  * weight (default: Ellipsis) specifies the weights to be used in the calculation. This may be
    None to specify that no weights should be used, or a property name or an array of weight
    values. Alternately, it may be a tuple (w1, w2) of the weights for datasets 1 and 2. If the
    argument is Ellipsis, then it will use weights if they are found in the retinotopy dataset;
    both datasets may contain weights in which the product is used.
  * weight_min (default: None) specifies the minimum weight a vertex must have to be included in
    the calculation.
  * visual_area (default: Ellipsis) specifies the visual area labels to be used in the
    calculation. This may be None to specify that no labels should be used, or a property name
    or an array of labels. Alternately, it may be a tuple (l1, l2) of the labels for datasets 1 
    and 2. If the argument is Ellipsis, then it will use labels if they are found in the
    retinotopy dataset; both datasets may contain labels in which the gold standard's labels are
    used if there is a gold standard and the overlapping labels are used otherwise.
  * visual_area_mask (default: None) specifies a list of visual areas included in the
    calculation; this is applied to all datasets with a visual_area key; see the 'visual_area'
    columns above and the visual_area option. If None, then no visual areas are filtered;
    otherwise, arguments should like (1,2,3), which would usually specify that areas V1, V2, and
    V3, be included.
  * gold (default: None) specifies which dataset should be considered the gold standard; this
    should be either 1 or 2. If a gold-standard dataset is specified, then it is used in certain
    calculations; for example, when scaling an error by eccentricity, the gold-standard's
    eccentricity will be used unless there is no gold standard, in which case the mean of the
    two values are used.