Plot groups of GOs which have been placed in sections.
3259
def plot_sections(self, fout_dir=".", **kws_usr):
        """"""
        kws_plt, _ = self._get_kws_plt(None, **kws_usr)
        PltGroupedGos(self).plot_sections(fout_dir, **kws_plt)
882561
def _goargs(self, ret, go_args):
        """"""
        goids = set()
        go2color = {}
        # Match on "GO ID" or "GO ID and color"
        re_gocolor = re.compile(r'(GO:\d{7})((?:#[0-9a-fA-F]{6})?)')
        for go_arg in go_args:
            mtch = re_gocolor.match(go_arg)
            if mtch:
                goid, color = mtch.groups()
                goids.add(goid)
                if color:
                    go2color[goid] = color
            else:
                print("WARNING: UNRECOGNIZED ARG({})".format(go_arg))
        self._update_ret(ret, goids, go2color)
1252733
def internal_get_goids_or_sections(self):
        """"""
        if self.goids_fin:
            chk_goids(self.goids_fin, "read_goids")
            return {'goids' : self.goids_fin}
        else:
            # Convert dict into 2D list retaining original section order
            sections_2d = []
            for section_name in self.sections_seen:
                if section_name in self.section2goids:
                    goids = self.section2goids.get(section_name)
                    chk_goids(goids, "GO IDs IN SECTION({S})".format(S=section_name))
                    sections_2d.append((section_name, goids))
            return {'sections' : sections_2d}
702075
def Subgroups(self):
		

		return(Groups(alias=self.alias,groups_lst=self.data['groups'],session=self.session))
883680
def _plt_gogrouped(self, goids, go2color_usr, **kws):
        """"""
        fout_img = self.get_outfile(kws['outfile'], goids)
        sections = read_sections(kws['sections'], exclude_ungrouped=True)
        # print ("KWWSSSSSSSS", kws)
        # kws_plt = {k:v for k, v in kws.items if k in self.kws_plt}
        grprobj_cur = self._get_grprobj(goids, sections)
        # GO: purple=hdr-only, green=hdr&usr, yellow=usr-only
        # BORDER: Black=hdr Blu=hdr&usr
        grpcolor = GrouperColors(grprobj_cur)  # get_bordercolor get_go2color_users
        grp_go2color = grpcolor.get_go2color_users()
        grp_go2bordercolor = grpcolor.get_bordercolor()
        for goid, color in go2color_usr.items():
            grp_go2color[goid] = color
        objcolor = Go2Color(self.gosubdag, objgoea=None,
                            go2color=grp_go2color, go2bordercolor=grp_go2bordercolor)
        go2txt = GrouperPlot.get_go2txt(grprobj_cur, grp_go2color, grp_go2bordercolor)
        objplt = GoSubDagPlot(self.gosubdag, Go2Color=objcolor, go2txt=go2txt, **kws)
        objplt.prt_goids(sys.stdout)
        objplt.plt_dag(fout_img)
        sys.stdout.write("{N:>6} sections read\n".format(
            N="NO" if sections is None else len(sections)))
        return fout_img
939616
def scatterplot(self, x, y, **kw):
        
        self.panel.scatterplot(x, y, **kw)
271972
def _summary_matching_gos(prt, pattern, matching_gos, all_gos):
        """"""
        msg = 'Found {N} GO(s) out of {M} matching pattern("{P}")\n'
        num_gos = len(matching_gos)
        num_all = len(all_gos)
        prt.write(msg.format(N=num_gos, M=num_all, P=pattern))
277132
def plot_grouped_gos(self, fout_img=None, exclude_hdrs=None, **kws_usr):
        """"""
        # kws_plt -> go2color go2bordercolor
        kws_plt, kws_dag = self._get_kws_plt(self.grprobj.usrgos, **kws_usr)
        pltgosusr = self.grprobj.usrgos
        if exclude_hdrs is not None:
            pltgosusr = pltgosusr.difference(self.grprobj.get_usrgos_g_hdrgos(exclude_hdrs))
        if fout_img is None:
            fout_img = "{GRP_NAME}.png".format(GRP_NAME=self.grprobj.grpname)
        # Split one plot into potentially three (BP, MF, CC) if png filename contains '{NS}'
        if '{NS}' in fout_img:
            go2nt = self.grprobj.gosubdag.get_go2nt(pltgosusr)
            for namespace in ['BP', 'MF', 'CC']:
                pltgos_ns = [go for go in pltgosusr if go2nt[go].NS == namespace]
                if pltgos_ns:
                    png = fout_img.format(NS=namespace)
                    self._plot_grouped_gos(png, pltgos_ns, kws_plt, kws_dag)
        # Plot all user GO IDs into a single plot, regardless of their namespace
        else:
            self._plot_grouped_gos(fout_img, pltgosusr, kws_plt, kws_dag)
505584
def _group_directional(self, clusters, adj_list, counts):
        ''' '''

        observed = set()
        groups = []
        for cluster in clusters:
            if len(cluster) == 1:
                groups.append(list(cluster))
                observed.update(cluster)
            else:
                cluster = sorted(cluster, key=lambda x: counts[x],
                                 reverse=True)
                # need to remove any node which has already been observed
                temp_cluster = []
                for node in cluster:
                    if node not in observed:
                        temp_cluster.append(node)
                        observed.add(node)
                groups.append(temp_cluster)

        return groups
698601
def multicategory_scatterplot(output_directory, file_prefix, df,
                              x_series_index, y_series_index, category_series_index,
                              series_color, plot_title = '',
                              x_axis_label = '', y_axis_label = '',
                              min_predicted_ddg = None, max_predicted_ddg = None, min_experimental_ddg = None, max_experimental_ddg = None):
    ''''''

    # todo: Abstract this graph from the current usage (DDG measurements).
    # todo: make the capped value for unquantified but classified measurements (e.g. DDG > 7 kcal/mol) parameterizable
    # todo: add an option to identify outliers by standard deviations (over the set of errors |x - y|) rather than by fixed value
    # todo: add an option to use geom_text_repel to avoid/reduce overlapping text
    # todo: allow users to provide colors for the facets / categories

    # Changeset
    # todo: Change it to take in a pandas dataframe instead of the data_table_headers + data_table parameters.
    # todo: Add exception if number of cases > 2 so the general case can be implemented once we have test data.
    # todo: use one column as the category e.g. "PDB". assert that there is a maximum number of categories. Test with > 2 categories
    # todo: remove all references to SNX27 and NHERF1 below and loop over the set of categories instead

    #print(df[facet_index])
    color_map = {}
    categories = list(df.ix[:, category_series_index].unique())
    print(type(categories))
    num_categories = len(categories)
    category_colors = get_spaced_plot_colors(num_categories)
    for x in xrange(num_categories):
        color_map[categories[x]] = '#' + category_colors[x]

    df['CategorizationColor'] = df.apply(lambda r: color_map[r[category_series_index]], axis = 1)
    categorization_color_index = len(df.columns.values) - 1

    # Monday: continue here
    print(df)
    sys.exit(0)
    try: os.mkdir(output_directory)
    except: pass
    assert(os.path.exists(output_directory))


    df['Categorization'] = df.apply(lambda r: _determine_fraction_correct_class(r[x_series_index], r[y_series_index])[0], axis = 1)
    categorization_index = len(df.columns.values) - 1
    df['CategorizationShape'] = df.apply(lambda r: _determine_fraction_correct_class(r[x_series_index], r[y_series_index])[1], axis = 1)
    categorization_shape_index = len(df.columns.values) - 1




    # Create the R script
    boxplot_r_script = '''
library(ggplot2)
library(gridExtra)
library(scales)
library(qualV)

# PNG generation
png('%(file_prefix)s.png', width=2560, height=2048, bg="white", res=600)
txtalpha <- 0.6
redtxtalpha <- 0.6

%(png_plot_commands)s
    '''

    xy_table_filename = '{0}.txt'.format(file_prefix)
    xy_table_filepath = os.path.join(output_directory, xy_table_filename)
    write_file(xy_table_filepath, '\n'.join(','.join(map(str, line)) for line in [data_table_headers] + data_table))

    single_plot_commands = '''
# Set the margins
par(mar=c(5, 5, 1, 1))

xy_data <- read.csv('%(xy_table_filename)s', header=T)

names(xy_data)[%(x_series_index)d + 1] <- "xvalues"
names(xy_data)[%(y_series_index)d + 1] <- "yvalues"

# coefs contains two values: (Intercept) and yvalues
coefs <- coef(lm(xvalues~yvalues, data = xy_data))
fitcoefs = coef(lm(xvalues~0 + yvalues, data = xy_data))
fitlmv_yvalues <- as.numeric(fitcoefs[1])
lmv_intercept <- as.numeric(coefs[1])
lmv_yvalues <- as.numeric(coefs[2])
lm(xy_data$yvalues~xy_data$xvalues)

xlabel <- "%(x_axis_label)s"
ylabel <- "%(y_axis_label)s"
plot_title <- "%(plot_title)s"
rvalue <- cor(xy_data$yvalues, xy_data$xvalues)
rvalue
xy_data

#3QDO = SNX27
#1G9O = NHERF1

valid_xy_data <- xy_data[which(xy_data$xvalues < 6.99),]
rvalue <- cor(valid_xy_data$yvalues, valid_xy_data$xvalues)
rvalue
valid_xy_data

valid_xy_data_NHERF1 <- xy_data[which(xy_data$xvalues < 6.99 & xy_data$PDB == '1G9O'),]
rvalue_NHERF1 <- cor(valid_xy_data_NHERF1$yvalues, valid_xy_data_NHERF1$xvalues)
rvalue_NHERF1
valid_xy_data_NHERF1

coefs_NHERF1 <- coef(lm(xvalues~yvalues, data = valid_xy_data_NHERF1))
lmv_intercept_NHERF1 <- as.numeric(coefs_NHERF1[1])
lmv_yvalues_NHERF1 <- as.numeric(coefs_NHERF1[2])

valid_xy_data_SNX27 <- xy_data[which(xy_data$xvalues < 6.99 & xy_data$PDB == '3QDO'),]
rvalue_SNX27 <- cor(valid_xy_data_SNX27$yvalues, valid_xy_data_SNX27$xvalues)
rvalue_SNX27
valid_xy_data_SNX27

coefs_SNX27 <- coef(lm(xvalues~yvalues, data = valid_xy_data_SNX27))
lmv_intercept_SNX27 <- as.numeric(coefs_SNX27[1])
lmv_yvalues_SNX27 <- as.numeric(coefs_SNX27[2])

lmv_intercept
lmv_yvalues
lmv_intercept_NHERF1
lmv_yvalues_NHERF1
lmv_intercept_SNX27
lmv_yvalues_SNX27

# Set graph limits and the position for the correlation value

minx <- min(0.0, min(xy_data$xvalues) - 0.1)
miny <- min(0.0, min(xy_data$yvalues) - 0.1)
maxx <- max(1.0, max(xy_data$xvalues) + 0.1)
maxy <- max(1.0, max(xy_data$yvalues) + 0.1)
    '''

    if min_predicted_ddg != None:
        single_plot_commands += '''
miny <- min(miny  - 0.2, %(min_predicted_ddg)f  - 0.2)
    '''
    if max_predicted_ddg != None:
        single_plot_commands += '''
maxy <- max(maxy + 0.5, %(max_predicted_ddg)f  + 0.5)

miny <- -6
maxy <- 12.5

    '''
    if min_experimental_ddg != None:
        single_plot_commands += '''
    minx <- min(minx, %(min_experimental_ddg)f)
        '''
    if max_experimental_ddg != None:
        single_plot_commands += '''
    maxx <- max(maxx, %(max_experimental_ddg)f) + 0.2
        '''

    single_plot_commands += '''
xpos <- minx + 0.2
ypos <- maxy - 1
ypos_SNX27 <- ypos - 1
ypos_NHERF1 <- ypos_SNX27 - 1

lrt <- expression('R'^tst)

p <- qplot(main="", xvalues, yvalues, data=xy_data, xlab=xlabel, ylab=ylabel, shape = PDB, alpha = I(txtalpha)) +
        geom_point(aes(color = PDB), alpha = 0.6) +
        scale_colour_manual(name="", values = c("1G9O"="orange", "3QDO"="blue", "3"="red", "value3"="grey", "value2"="black")) +
        labs(title = "%(plot_title)s") +
        theme(plot.title = element_text(color = "#555555", size=rel(0.75))) +

        # Correlation fit lines (global + one per facet
        geom_abline(size = 0.125, color="black", intercept = lmv_intercept, slope = lmv_yvalues, alpha=0.2) +
        geom_abline(size = 0.125, color="orange", intercept = lmv_intercept_NHERF1, slope = lmv_yvalues_NHERF1, alpha=0.4) +
        geom_abline(size = 0.125, color="blue", intercept = lmv_intercept_SNX27, slope = lmv_yvalues_SNX27, alpha=0.4) +

        geom_abline(slope=1, intercept=0, linetype=3, size=0.25, alpha=0.4) + # add a diagonal (dotted)
        coord_cartesian(xlim = c(minx, maxx), ylim = c(miny, maxy)) + # set the graph limits

        geom_text(hjust = 0, size=1.5, color="#000000", alpha=0.6, data=subset(xy_data, abs(yvalues - xvalues) > 2 & xvalues <= 0), aes(xvalues, yvalues+0.35, label=Origin_of_peptide), check_overlap = TRUE) + # label outliers
        geom_text(hjust = 1, size=1.5, color="#000000", alpha=0.6, data=subset(xy_data, abs(yvalues - xvalues) > 2 & xvalues > 0), aes(xvalues, yvalues+0.35, label=Origin_of_peptide), check_overlap = TRUE) + # label outliers
        geom_text(hjust=0, size=2, colour="black", aes(x = xpos, y = ypos, label = sprintf("R == %%0.2f", round(rvalue, digits = 4))), parse = TRUE) +
        geom_text(hjust=0, size=2, colour="darkorange", aes(x = xpos, y = ypos_NHERF1, label = sprintf("R[NHERF] == %%0.2f", round(rvalue_NHERF1, digits = 4))), parse = TRUE) +
        geom_text(hjust=0, size=2, colour="blue", aes(x = xpos, y = ypos_SNX27, label = sprintf("R[SNX27] == %%0.2f", round(rvalue_SNX27, digits = 4))), parse = TRUE) +
        theme(legend.position = "none")
#       geom_text(hjust=0, size=2, colour="black", aes(xpos, ypos, fontface="plain", family = "sans", label=paste(sprintf("R = %%0.2f%%s", round(rvalue, digits = 4), lrt), expression('R'[3])  ))) # add correlation text; hjust=0 sets left-alignment

#geom_text(hjust=0, size=3, colour="black", aes(xpos, ypos, fontface="plain", family = "sans", label=sprintf("R = %%0.2f", round(rvalue, digits = 4)))) # add correlation text; hjust=0 sets left-alignment
#       geom_text(hjust=0, size=3, colour="black", aes(xpos, ypos, fontface="plain", family = "sans", label=sprintf("R = %%0.2f", round(rvalue, digits = 4)))) # add correlation text; hjust=0 sets left-alignment

# Plot graph
p
dev.off()
        '''


    #geom_point(aes(color = C)) +
    #color = "%(series_color)s"

    # Create the R script
    plot_type = 'png'
    png_plot_commands = single_plot_commands % locals()
    boxplot_r_script = boxplot_r_script % locals()
    r_script_filename = '{0}.R'.format(file_prefix)
    r_script_filepath = os.path.join(output_directory, r_script_filename)
    write_file(r_script_filepath, boxplot_r_script)

    # Run the R script
    run_r_script(r_script_filename, cwd = output_directory)
356250
def plot_summary(self, cycles=None):
        """"""
        if cycles is None:
            cycles = [0]
        fig1 = plt.figure()
        ax1 = fig1.add_subplot(221)
        ax1.set_title('Fit')
        ax2 = fig1.add_subplot(222)
        ax2.set_title('OCV')
        ax3 = fig1.add_subplot(223)
        ax3.set_title('Tau')
        ax3.set_yscale("log")
        ax4 = fig1.add_subplot(224)
        ax4.set_title('Voltage Impact')

        plot_data = self.get_best_fit_data()
        for cycle in cycles:
            ax1.plot(plot_data[cycle][0], plot_data[cycle][1])
            ax1.plot(plot_data[cycle][0], plot_data[cycle][2])

        plot_data = self.get_best_fit_parameters_grouped()

        for i in range(self.circuits):
            ax3.plot(self.get_fit_cycles(), plot_data['t' + str(i)])
            ax4.plot(self.get_fit_cycles(), plot_data['w' + str(i)])

        ax2.plot(self.get_fit_cycles(), plot_data['ocv'])
776761
def get_nts_sections(self, sections, sortby=None):
        """"""
        goids = self.get_goids_sections(sections)
        gosubdag = GoSubDag(goids, self.go2obj)
        return [(sec, gosubdag.get_nts(gos, sortby)) for sec, gos in sections]
18056
def _rm_gos_edges(rm_goids, edges_all):
        """"""
        edges_reduced = []
        for goid_child, goid_parent in sorted(edges_all, key=lambda t: t[1]):
            if goid_child not in rm_goids and goid_parent not in rm_goids:
                edges_reduced.append((goid_child, goid_parent))
        return edges_reduced
453102
def get_go2txt(grprobj_cur, grp_go2color, grp_go2bordercolor):
        """"""
        goids_main = set(o.id for o in grprobj_cur.gosubdag.go2obj.values())
        hdrobj = grprobj_cur.hdrobj
        grprobj_all = Grouper("all",
                              grprobj_cur.usrgos.union(goids_main), hdrobj, grprobj_cur.gosubdag)
        # Adds section text to all GO terms in plot (misses middle GO terms)
        _secdflt = hdrobj.secdflt
        _hilight = set(grp_go2color.keys()).union(grp_go2bordercolor)
        ret_go2txt = {}
        # Keep sections text only if GO header, GO user, or not Misc.
        if hdrobj.sections:
            for goid, txt in grprobj_all.get_go2sectiontxt().items():
                if txt == 'broad':
                    continue
                if txt != _secdflt or goid in _hilight:
                    ret_go2txt[goid] = txt
        return ret_go2txt
1267497
def main():
    
    fmt,plot='svg',0
    if '-h' in sys.argv: # check if help is needed
        print(main.__doc__)
        sys.exit() # graceful quit
    elif '-f' in sys.argv: # ask for filename
        ind=sys.argv.index('-f')
        file=sys.argv[ind+1]
        f=open(file,'r')
        input=f.readlines()
    Data=[]
    for line in input:
        line.replace('\n','')
        if '\t' in line:   # read in the data from standard input
            rec=line.split('\t') # split each line on space to get records
        else:
            rec=line.split() # split each line on space to get records
        Data.append(float(rec[0]))
    
#
    if len(Data) >=10: 
        QQ={'unf1':1}
        pmagplotlib.plot_init(QQ['unf1'],5,5)
        pmagplotlib.plot_qq_unf(QQ['unf1'],Data,'QQ-Uniform') # make plot
    else:
        print('you need N> 10')
        sys.exit()
    pmagplotlib.draw_figs(QQ) 
    files={}
    for key in list(QQ.keys()):
        files[key]=key+'.'+fmt 
    if pmagplotlib.isServer:
        black     = '#000000'
        purple    = '#800080'
        titles={}
        titles['eq']='Equal Area Plot'
        EQ = pmagplotlib.add_borders(EQ,titles,black,purple)
        pmagplotlib.save_plots(QQ,files)
    elif plot==1:
        files['qq']=file+'.'+fmt 
        pmagplotlib.save_plots(QQ,files)
    else:
        ans=input(" S[a]ve to save plot, [q]uit without saving:  ")
        if ans=="a": pmagplotlib.save_plots(QQ,files)
1263587
private void placeMultipleGroups(IAtomContainer mol) {
        final List<Sgroup> sgroups = mol.getProperty(CDKConstants.CTAB_SGROUPS);
        if (sgroups == null)
            return;
        final List<Sgroup> multipleGroups = new ArrayList<>();
        for (Sgroup sgroup : sgroups) {
            if (sgroup.getType() == SgroupType.CtabMultipleGroup)
                multipleGroups.add(sgroup);
        }
        if (multipleGroups.isEmpty())
            return;

        int[][] adjlist = GraphUtil.toAdjList(mol);
        Map<IAtom,Integer> idxs = new HashMap<>();
        for (IAtom atom : mol.atoms())
            idxs.put(atom, idxs.size());

        for (Sgroup sgroup : multipleGroups) {
            final int numCrossing = sgroup.getBonds().size();
            if (numCrossing != 0 && numCrossing != 2)
                continue;

            // extract substructure
            final IAtomContainer substructure = mol.getBuilder().newInstance(IAtomContainer.class);
            final Set<IAtom> visit = new HashSet<>();
            final Collection<IAtom> patoms = sgroup.getValue(SgroupKey.CtabParentAtomList);
            if (patoms == null)
                continue;
            for (IAtom atom : patoms) {
                substructure.addAtom(atom);
                visit.add(atom);
            }
            for (IBond bond : mol.bonds()) {
                IAtom beg = bond.getBegin();
                IAtom end = bond.getEnd();
                if (visit.contains(beg) && visit.contains(end))
                    substructure.addBond(bond);
            }

            // advanced API usage, we make a set that only includes the atoms we want to match
            // and use this in a custom AtomMatcher to skip matches we don't want and update as
            // we go
            visit.addAll(sgroup.getAtoms());

            Pattern ptrn = VentoFoggia.findSubstructure(substructure, new AtomMatcher() {
                @Override
                public boolean matches(IAtom a, IAtom b) {
                    if (!visit.contains(b))
                        return false;
                    final int aElem = safeUnbox(a.getAtomicNumber());
                    final int bElem = safeUnbox(b.getAtomicNumber());
                    if (aElem != bElem)
                        return false;
                    final int aChg = safeUnbox(a.getFormalCharge());
                    final int bChg = safeUnbox(b.getFormalCharge());
                    if (aChg != bChg)
                        return false;
                    final int aMass = safeUnbox(a.getMassNumber());
                    final int bMass = safeUnbox(b.getMassNumber());
                    if (aMass != bMass)
                        return false;
                    final int aHcnt = safeUnbox(a.getImplicitHydrogenCount());
                    final int bHcnt = safeUnbox(b.getImplicitHydrogenCount());
                    if (aHcnt != bHcnt)
                        return false;
                    return true;
                }
            }, BondMatcher.forOrder());

            Set<IAtom> sgroupAtoms = sgroup.getAtoms();

            // when there are crossing bonds, things are more tricky as
            // we need to translate connected parts
            List<Map.Entry<Point2d,Vector2d>> outgoing = new ArrayList<>();
            List<Map.Entry<IBond,Vector2d>>   xBondVec = new ArrayList<>();
            if (numCrossing == 2) {
                for (IBond bond : mol.bonds()) {
                    IAtom beg = bond.getBegin();
                    IAtom end = bond.getEnd();
                    if (patoms.contains(beg) == patoms.contains(end))
                        continue;
                    if (patoms.contains(beg)) {
                        outgoing.add(new SimpleImmutableEntry<>(beg.getPoint2d(),
                                                                new Vector2d(end.getPoint2d().x - beg.getPoint2d().x,
                                                                             end.getPoint2d().y - beg.getPoint2d().y)));
                    } else {
                        outgoing.add(new SimpleImmutableEntry<>(end.getPoint2d(),
                                                                new Vector2d(beg.getPoint2d().x - end.getPoint2d().x,
                                                                             beg.getPoint2d().y - end.getPoint2d().y)));
                    }
                }
                for (IBond bond : sgroup.getBonds()) {
                    IAtom beg = bond.getBegin();
                    IAtom end = bond.getEnd();
                    if (sgroupAtoms.contains(beg)) {
                        xBondVec.add(new SimpleImmutableEntry<>(bond,
                                                                new Vector2d(end.getPoint2d().x - beg.getPoint2d().x,
                                                                             end.getPoint2d().y - beg.getPoint2d().y)));
                    } else {
                        xBondVec.add(new SimpleImmutableEntry<>(bond,
                                                                new Vector2d(beg.getPoint2d().x - end.getPoint2d().x,
                                                                             beg.getPoint2d().y - end.getPoint2d().y)));
                    }
                }
            }

            // no crossing bonds is easy just map the repeat part and transfer coordinates
            visit.removeAll(patoms); // don't need to map parent
            for (Map<IAtom, IAtom> atoms : ptrn.matchAll(mol).uniqueAtoms().toAtomMap()) {
                for (Map.Entry<IAtom, IAtom> e : atoms.entrySet()) {
                    e.getValue().setPoint2d(new Point2d(e.getKey().getPoint2d()));
                }
                // search is lazy so can update the matcher before the next match
                // is found (implementation ninja)
                visit.removeAll(atoms.values());
            }

            // reposition
            assert xBondVec.size() == outgoing.size();
            for (Map.Entry<IBond,Vector2d> e : xBondVec) {
                IBond bond = e.getKey();

                // can't fix move ring bonds
                if (bond.isInRing())
                    continue;

                IAtom beg  = sgroupAtoms.contains(bond.getBegin()) ? bond.getBegin() : bond.getEnd();
                Map.Entry<Point2d,Vector2d> best = null;
                for (Map.Entry<Point2d,Vector2d> candidate : outgoing) {
                    if (best == null || candidate.getKey().distance(beg.getPoint2d()) < best.getKey().distance(beg.getPoint2d()))
                        best = candidate;
                }
                outgoing.remove(best);
                assert best != null;

                // visit rest of connected molecule
                Set<Integer> iVisit = new HashSet<>();
                iVisit.add(idxs.get(beg));
                visit(iVisit, adjlist, idxs.get(bond.getOther(beg)));
                iVisit.remove(idxs.get(beg));
                IAtomContainer frag = mol.getBuilder().newInstance(IAtomContainer.class);
                for (Integer idx : iVisit)
                    frag.addAtom(mol.getAtom(idx));

                Vector2d orgVec = e.getValue();
                Vector2d newVec = best.getValue();

                Point2d endP    = bond.getOther(beg).getPoint2d();
                Point2d newEndP = new Point2d(beg.getPoint2d());
                newEndP.add(newVec);

                // need perpendicular dot product to get signed angle
                double pDot  = orgVec.x * newVec.y - orgVec.y * newVec.x;
                double theta = Math.atan2(pDot, newVec.dot(orgVec));

                // position
                GeometryUtil.translate2D(frag, newEndP.x - endP.x, newEndP.y - endP.y);
                GeometryUtil.rotate(frag, new Point2d(bond.getOther(beg).getPoint2d()), theta);
            }
        }

    }
333184
def _plot(self):
        """"""
        for serie in self.series[::-1 if self.stack_from_top else 1]:
            self.bar(serie)
        for serie in self.secondary_series[::-1 if self.stack_from_top else 1]:
            self.bar(serie, True)