source: tags/arb-6.0/DIST/DI_foundclusters.cxx

Last change on this file was 11991, checked in by westram, 10 years ago
File size: 14.7 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : DI_foundclusters.cxx                               //
4//   Purpose   : Store results of cluster detection                 //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2009   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#include "di_foundclusters.hxx"
13#include "di_clustertree.hxx"
14
15#include <aw_window.hxx>
16#include <aw_select.hxx>
17
18#include <cmath>
19
20using namespace std;
21
22ID Cluster::unused_id = 1;
23
24static AP_FLOAT calc_mean_dist_to(const ClusterTree *distance_to, const LeafRelations *distances) {
25    size_t   count = 0;
26    AP_FLOAT sum   = 0.0;
27
28    LeafRelationCIter dist_end = distances->end();
29    for (LeafRelationCIter dist = distances->begin(); dist != dist_end; ++dist) {
30        const TwoLeafs& leafs = dist->first;
31
32        if (leafs.first() == distance_to || leafs.second() == distance_to) {
33            count++;
34            sum += dist->second;
35        }
36    }
37
38    cl_assert(count);                               // 'distance_to' does not occur in 'distances'
39
40    return sum/count;
41}
42
43Cluster::Cluster(ClusterTree *ct)
44    : next_desc(NULL)
45{
46    cl_assert(ct->get_state() == CS_IS_CLUSTER);
47
48    id = unused_id++;
49
50    const LeafRelations *distances = ct->get_sequence_dists();
51    cl_assert(distances);
52
53    LeafRelationCIter dist     = distances->begin();
54    LeafRelationCIter dist_end = distances->end();
55
56    cl_assert(dist != dist_end); // oops - empty cluster
57
58    min_dist  = max_dist = dist->second;
59    mean_dist = 0.0;
60
61    AP_FLOAT min_mean_dist = 9999999999.0;
62    representative         = NULL;
63
64    for (; dist != dist_end; ++dist) {
65        const TwoLeafs& leafs = dist->first;
66        AP_FLOAT        di    = dist->second;
67
68        if (di<min_dist) min_dist = di;
69        if (di>max_dist) max_dist = di;
70
71        mean_dist += di;
72
73        for (int i = 0; i<2; ++i) {
74            const ClusterTree *member     = i ? leafs.first() : leafs.second();
75            GBDATA            *gb_species = member->gb_node;
76
77            cl_assert(gb_species);
78            if (members.find(gb_species) == members.end()) { // not in set
79                members.insert(gb_species);
80                AP_FLOAT mean_dist_to_member = calc_mean_dist_to(member, distances);
81
82                if (mean_dist_to_member<min_mean_dist) {
83                    representative = gb_species;
84                    min_mean_dist  = mean_dist_to_member;
85                }
86            }
87        }
88
89    }
90
91    cl_assert(members.size() == ct->get_leaf_count());
92
93    min_bases = ct->get_min_bases();
94    {
95        ClusterTree *root    = ct->get_root_node();
96        size_t       allSize = root->get_leaf_count();
97        size_t       size    = ct->get_leaf_count();
98        size_t       first   = ct->relative_position_in(root);
99        size_t       last    = first+size-1;
100
101        rel_tree_pos = ((double(first)+last)/2) / allSize;
102    }
103    mean_dist /= distances->size();
104    cl_assert(representative);
105    desc = create_description(ct);
106}
107
108static string *get_downgroups(const ARB_countedTree *ct, const ARB_tree_predicate& keep_group_name, size_t& group_members) {
109    string *result = NULL;
110    if (ct->is_leaf) {
111        group_members = 0;
112    }
113    else {
114        const char *group_name = ct->get_group_name();
115
116        if (group_name && keep_group_name.selects(*ct)) {
117            group_members = ct->get_leaf_count();
118            result        = new string(ct->name);
119        }
120        else {
121            string *leftgroups  = get_downgroups(ct->get_leftson(), keep_group_name, group_members);
122            size_t  right_members;
123            string *rightgroups = get_downgroups(ct->get_rightson(), keep_group_name, right_members);
124
125            group_members += right_members;
126
127            if (leftgroups || rightgroups) {
128                if (!leftgroups)       { result = rightgroups; rightgroups = NULL; }
129                else if (!rightgroups) { result = leftgroups; leftgroups = NULL; }
130                else                     result = new string(*leftgroups+"+"+*rightgroups);
131
132                delete leftgroups;
133                delete rightgroups;
134            }
135        }
136    }
137    return result;
138}
139static const char *get_upgroup(const ARB_countedTree *ct, const ARB_tree_predicate& keep_group_name, const ARB_countedTree*& upgroupPtr, bool reuse_original_name) {
140    const char *group_name = ct->get_group_name();
141    char       *original   = (reuse_original_name && group_name) ? originalGroupName(group_name) : NULL;
142
143    if (original) {
144        static SmartCharPtr result;
145        result = original;
146        group_name = &*result;
147        upgroupPtr = ct;
148    }
149    else if (group_name && keep_group_name.selects(*ct)) {
150        upgroupPtr = ct;
151    }
152    else {
153        const ARB_countedTree *father = ct->get_father();
154        if (father) {
155            group_name = get_upgroup(father, keep_group_name, upgroupPtr, reuse_original_name);
156        }
157        else {
158            upgroupPtr = ct;
159            group_name = "WHOLE_TREE";
160        }
161    }
162
163    cl_assert(!upgroupPtr || ct->is_inside(upgroupPtr));
164    cl_assert(group_name);
165    return group_name;
166}
167
168string Cluster::create_description(const ARB_countedTree *ct) {
169    // creates cluster description (using up- and down-groups, percentages, existing groups, ...)
170    cl_assert(!ct->is_leaf);
171
172    string name;
173
174    UseAnyTree use_any_upgroup;
175
176    const ARB_countedTree *upgroup         = NULL;
177    const char            *upgroup_name    = get_upgroup(ct, use_any_upgroup, upgroup, false);
178    size_t                 upgroup_members = upgroup->get_leaf_count();
179
180    size_t cluster_members = ct->get_leaf_count();
181
182    cl_assert(upgroup_members>0); // if no group, whole tree should have been returned
183
184    if (upgroup_members == cluster_members) { // use original or existing name
185        name = string("[G] ")+upgroup_name;
186    }
187    else {
188        size_t  downgroup_members;
189        string *downgroup_names = get_downgroups(ct, use_any_upgroup, downgroup_members);
190
191        AP_FLOAT up_rel   = (AP_FLOAT)cluster_members/upgroup_members;   // used for: xx% of upgroup (big is good)
192        AP_FLOAT down_rel = (AP_FLOAT)downgroup_members/cluster_members; // used for: downgroup(s) + (1-xx)% outgroup (big is good)
193
194        if (up_rel>down_rel) { // prefer up_percent
195            name = string(GBS_global_string("%4.1f%% of %s", up_rel*100.0, upgroup_name));
196        }
197        else {
198            if (downgroup_members == cluster_members) {
199                name = downgroup_names->c_str();
200            }
201            else {
202                name = string(GBS_global_string("%s + %4.1f%% outgroup", downgroup_names->c_str(), (1-down_rel)*100.0));
203            }
204        }
205
206        delete downgroup_names;
207    }
208    return name;
209}
210
211string Cluster::get_upgroup_info(const ARB_countedTree *ct, const ARB_tree_predicate& keep_group_name) {
212    // generates a string indicating relative position in up-group
213    cl_assert(!ct->is_leaf);
214
215    const ARB_countedTree *upgroup      = NULL;
216    const char            *upgroup_name = get_upgroup(ct, keep_group_name, upgroup, true);
217
218    size_t upgroup_members = upgroup->get_leaf_count();
219    size_t cluster_members = ct->get_leaf_count();
220
221    const char *upgroup_info;
222    if (upgroup_members == cluster_members) {
223        upgroup_info = upgroup_name;
224    }
225    else {
226        size_t cluster_pos1 = ct->relative_position_in(upgroup)+1; // range [1..upgroup_members]
227        size_t cluster_posN = cluster_pos1+cluster_members-1;
228
229        upgroup_info = GBS_global_string("%zu-%zu/%zu_%s", cluster_pos1, cluster_posN, upgroup_members, upgroup_name);
230    }
231    return upgroup_info;
232}
233
234// --------------------------
235//      DisplayFormat
236
237class DisplayFormat : virtual Noncopyable {
238    size_t   max_count;
239    AP_FLOAT max_dist;
240    AP_FLOAT max_minBases;
241
242    mutable char *format_string;
243
244    static long calc_digits(long val) {
245        long digits;
246        if (val>0) digits      = log10(val)+1;
247        else if (val<0) digits = calc_digits(-val);
248        else digits            = 1;
249
250        cl_assert(digits>0);
251       
252        return digits;
253    }
254
255    static char *make_format(size_t val) {
256        long digits = calc_digits(val);
257        return GBS_global_string_copy("%%%lizu", digits);
258    }
259    static char *make_format(AP_FLOAT val) {
260        long digits   = calc_digits(long(val));
261        long afterdot = digits <=3 ? 5-digits-1 : 0;
262
263        cl_assert(afterdot >= 0);
264        long all = digits+(afterdot ? (afterdot+1) : 0);
265
266        return GBS_global_string_copy("%%%li.%lif", all, afterdot);
267    }
268
269public:
270
271    DisplayFormat()
272        : max_count(0)
273        , max_dist(0.0)
274        , max_minBases(0.0)
275        , format_string(NULL)
276    {}
277
278    ~DisplayFormat() {
279        free(format_string);
280    }
281
282    void announce(size_t count, AP_FLOAT maxDist, AP_FLOAT minBases) {
283        max_count    = std::max(max_count, count);
284        max_dist     = std::max(max_dist, maxDist);
285        max_minBases = std::max(max_minBases, minBases);
286
287        freenull(format_string);
288    }
289
290    const char *get_format() const {
291        if (!format_string) {
292            // create format string for description
293            char *count_part = make_format(max_count);
294            char *dist_part  = make_format(max_dist);
295            char *bases_part = make_format(max_minBases);
296
297            format_string = GBS_global_string_copy("%s  %s [%s-%s]  %s  %%s",
298                                                   count_part,
299                                                   dist_part, dist_part, dist_part,
300                                                   bases_part);
301
302            free(bases_part);
303            free(dist_part);
304            free(count_part);
305        }
306        return format_string;
307    }
308};
309
310
311
312void Cluster::scan_display_widths(DisplayFormat& format) const {
313    AP_FLOAT max_of_all_dists = std::max(max_dist, std::max(min_dist, mean_dist));
314    format.announce(get_member_count(), max_of_all_dists*100.0, min_bases);
315}
316
317const char *Cluster::get_list_display(const DisplayFormat *format) const {
318    const char *format_string;
319    if (format) format_string = format->get_format();
320    else        format_string = "%zu  %.3f [%.3f-%.3f]  %.1f  %s";
321
322    return GBS_global_string(format_string,
323                             get_member_count(),
324                             mean_dist*100.0,
325                             min_dist*100.0,
326                             max_dist*100.0,
327                             min_bases,
328                             desc.c_str());
329}
330
331
332// ---------------------
333//      ClustersData
334
335void ClustersData::add(ClusterPtr clus, ClusterSubset subset) {
336    known_clusters[clus->get_ID()] = clus;
337    get_subset(subset).push_back(clus->get_ID());
338}
339
340void ClustersData::remove(ClusterPtr clus, ClusterSubset subset) {
341    int pos = get_pos(clus, subset);
342    if (pos != -1) {
343        ClusterIDs&           ids      = get_subset(subset);
344        ClusterIDs::iterator  toRemove = ids.begin();
345        advance(toRemove, pos);
346        ids.erase(toRemove);
347        known_clusters.erase(clus->get_ID());
348    }
349}
350
351void ClustersData::clear(ClusterSubset subset) {
352    ClusterIDs&     ids    = get_subset(subset);
353    ClusterIDsIter  id_end = ids.end();
354    for (ClusterIDsIter  id = ids.begin(); id != id_end; ++id) {
355        known_clusters.erase(*id);
356    }
357    ids.clear();
358}
359
360void ClustersData::store(ID id) {
361    ClusterPtr toStore = clusterWithID(id);
362    remove(toStore, SHOWN_CLUSTERS);
363    add(toStore, STORED_CLUSTERS);
364}
365
366void ClustersData::store_all() {
367    copy(shown.begin(), shown.end(), back_insert_iterator<ClusterIDs>(stored));
368    shown.clear();
369    sort_needed = false;
370}
371
372void ClustersData::restore_all() {
373    copy(stored.begin(), stored.end(), back_insert_iterator<ClusterIDs>(shown));
374    stored.clear();
375    sort_needed = true;
376}
377
378void ClustersData::swap_all() {
379    swap(stored, shown);
380    sort_needed = true;
381}
382
383
384class SortClusterIDsBy {
385    const KnownClusters& known;
386    ClusterOrder         primary_order;
387    ClusterOrder         secondary_order;
388public:
389    SortClusterIDsBy(const KnownClusters& known_, ClusterOrder primary, ClusterOrder secondary)
390        : known(known_)
391        , primary_order(primary)
392        , secondary_order(secondary)
393    {}
394
395     bool operator()(ID id1, ID id2) const {
396         ClusterPtr cl1 = known.find(id1)->second;
397         ClusterPtr cl2 = known.find(id2)->second;
398
399         cl_assert(!cl1.isNull() && !cl2.isNull());
400
401         bool less = cl1->lessByOrder(*cl2, primary_order);
402         if (!less) {
403             bool equal = !cl2->lessByOrder(*cl1, primary_order);
404             if (equal) {
405                 less = cl1->lessByOrder(*cl2, secondary_order);
406             }
407         }
408         return less;
409     }
410};
411
412void ClustersData::update_cluster_selection_list() {
413    cl_assert(clusterList);
414    clusterList->clear();
415
416    if (shown.empty()) {
417        clusterList->insert_default("<No clusters detected>", ID(0));
418    }
419    else {
420        {
421            SortClusterIDsBy sortBy(known_clusters, criteria[0], criteria[1]);
422            sort(shown.begin(), shown.end(), sortBy);
423        }
424        {
425            ClusterIDsIter    cl_end = shown.end();
426            DisplayFormat format;
427
428            for (int pass = 1; pass <= 2; ++pass) {
429                for (ClusterIDsIter cl = shown.begin(); cl != cl_end; ++cl) {
430                    ID         id      = *cl;
431                    ClusterPtr cluster = clusterWithID(id);
432
433                    if (pass == 1) {
434                        cluster->scan_display_widths(format);
435                    }
436                    else {
437                        clusterList->insert(cluster->get_list_display(&format), cluster->get_ID());
438                    }
439                }
440            }
441            clusterList->insert_default("<select no cluster>", ID(0));
442        }
443    }
444    clusterList->update();
445}
446
447void ClustersData::free() {
448    // delete calculated/stored data
449    shown.clear();
450    stored.clear();
451    update_cluster_selection_list();
452    known_clusters.clear();
453}
454
455char *originalGroupName(const char *groupname) {
456    char *original = NULL;
457    const char *was = strstr(groupname, " {was:");
458    const char *closing = strchr(groupname, '}');
459    if (was && closing) {
460        original = GB_strpartdup(was+6, closing-1);
461        if (original[0]) {
462            char *sub = originalGroupName(original);
463            if (sub) freeset(original, sub);
464        }
465        else {
466            freenull(original); // empty -> invalid
467        }
468    }
469    return original;
470}
471
472
473
Note: See TracBrowser for help on using the repository browser.