/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/classgen.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/classgen.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 886 by jonathan, Fri May 9 16:36:56 2003 UTC revision 1098 by jonathan, Fri May 30 06:27:44 2003 UTC
# Line 5  Line 5 
5  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
6  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
7    
8    """
9    Functions to generate Classifications
10    """
11    
12    __version__ = "$Revision$"
13    # $Source$
14    # $Id$
15    
16  import operator  import operator
17    
18  from color import Color  from color import Color
# Line 12  from range import Range Line 20  from range import Range
20  from classification import Classification, ClassGroupSingleton, \  from classification import Classification, ClassGroupSingleton, \
21      ClassGroupRange, ClassGroupProperties      ClassGroupRange, ClassGroupProperties
22    
23  class ClassGenerator:  def GenSingletonsFromList(_list, numGroups, ramp):
24        """Generate a new classification consisting solely of singletons.
25    
26        The resulting classification will consist of at most 'numGroups'
27        groups whose group properties ramp between 'prop1' and 'prop2'. There
28        could be fewer groups if '_list' contains fewer that 'numGroups' items.
29    
30        _list -- any object that implements the iterator interface
31    
32        numGroups -- how many groups to generate. This can not be
33                     determined while the classification is being
34                     generated because the stepping values must
35                     be precalculated to ramp between prop1 and prop2.
36    
37        ramp -- an object which implements the CustomRamp interface
38        """
39    
40        clazz = Classification()
41        if numGroups == 0: return clazz
42    
43        ramp.SetNumGroups(numGroups)
44    
45      def GenSingletonsFromList(self, list, numGroups, ramp):      for value, prop in zip(_list, ramp):
46          """Generate a new classification consisting solely of singletons.          clazz.AppendGroup(ClassGroupSingleton(value, prop))
47    
48          The resulting classification will consist of at most 'numGroups'      return clazz
         groups whose group properties ramp between 'prop1' and 'prop2'. There  
         could be fewer groups if 'list' contains fewer that 'numGroups' items.  
49    
50          list -- any object that implements the iterator interface  def GenSingletons(min, max, numGroups, ramp):
51    
52          numGroups -- how many groups to generate. This can not be      clazz = Classification()
                      determined while the classification is being  
                      generated because the stepping values must  
                      be precalculated to ramp between prop1 and prop2.  
53    
54          prop1 -- initial group property values      #step = int((max - min) / float(numGroups))
55    
56          prop2 -- final group property values      if numGroups > 0:
         """  
57    
58          clazz = Classification()          step = int((max - min + 1) / float(numGroups))
59          if numGroups == 0: return clazz          cur_value = min
60    
61          ramp.SetNumGroups(numGroups)          ramp.SetNumGroups(numGroups)
62    
63          for value, prop in zip(list, ramp):          for prop in ramp:
64              clazz.AppendGroup(ClassGroupSingleton(value, prop))              clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)
65                cur_value += step
66    
67          return clazz      return clazz
68    
69      def GenSingletons(self, min, max, numGroups, ramp):  def GenUniformDistribution(min, max, numGroups,
70                               ramp, intStep = False):
71        """Generate a classification with numGroups range groups
72        each with the same interval.
73    
74          clazz = Classification()      intStep -- force the calculated stepping to an integer.
75                   Useful if the values are integers but the
76                   number of groups specified doesn't evenly
77                   divide (max - min).
78        """
79    
80          #step = int((max - min) / float(numGroups))      clazz = Classification()
81        if numGroups == 0: return clazz
82    
83          if numGroups > 0:      ramp.SetNumGroups(numGroups)
84    
85              step = int((max - min + 1) / float(numGroups))      step = (max - min) / float(numGroups)
             cur_value = min  
86    
87              ramp.SetNumGroups(numGroups)      if intStep:
88            step = int(step)
89    
90              for prop in ramp:      cur_min = min
91                  clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)      cur_max = cur_min + step
                 cur_value += step  
   
         return clazz  
   
     def GenUnifromDistribution(self, min, max, numGroups,  
                                ramp, intStep = False):  
         """Generate a classification with numGroups range groups  
         each with the same interval.  
   
         intStep -- force the calculated stepping to an integer.  
                    Useful if the values are integers but the  
                    number of groups specified doesn't evenly  
                    divide (max - min).  
         """  
92    
93          clazz = Classification()      i = 0
94          if numGroups == 0: return clazz      end = "["
95        for prop in ramp:
96    
97          ramp.SetNumGroups(numGroups)          if i == (numGroups - 1):
98                cur_max = max
99                end = "]"
100    
         step = (max - min) / float(numGroups)  
101    
102          if intStep:          # this check guards against rounding issues
103              step = int(step)          if cur_min != cur_max:
104                range = Range(("[", cur_min, cur_max, end))
105                clazz.AppendGroup(ClassGroupRange(range, None, prop))
106    
107          cur_min = min          cur_min = cur_max
108          cur_max = cur_min + step          cur_max += step
109            i += 1
110    
111          i = 0      return clazz
         end = "["  
         for prop in ramp:  
112    
             if i == (numGroups - 1):  
                 cur_max = max  
                 end = "]"  
113    
114    def GenQuantiles(_list, percents, ramp, _range):
115        """Generates a Classification which has groups of ranges that
116        represent quantiles of _list at the percentages given in percents.
117        Only the values that fall within _range are considered.
118    
119              # this check guards against rounding issues      Returns a tuple (adjusted, Classification) where adjusted is
120              if cur_min != cur_max:      True if the Classification does not exactly represent the given
121                  range = Range("[" + str(float(cur_min)) + ";" +      range, or if the Classification is empty.
                                     str(float(cur_max)) + end)  
                 clazz.AppendGroup(ClassGroupRange(range, None, prop))  
122    
123              cur_min = cur_max      _list -- a sort list of values
             cur_max += step  
             i += 1  
124    
125          return clazz      percents -- a sorted list of floats in the range 0.0-1.0 which
126                    represent the upper bound of each quantile
127    
128        ramp -- an object which implements the CustomRamp interface
129    
130      def GenQuantiles(self, list, percents, ramp, _range):      _range -- a Range object
131          clazz = Classification()      """
         quantiles = self.CalculateQuantiles(list, percents, _range)  
         numGroups = len(quantiles[1])  
         if numGroups == 0: return clazz  
132    
133          ramp.SetNumGroups(numGroups)      clazz = Classification()
134        quantiles = CalculateQuantiles(_list, percents, _range)
135        adjusted = True
136    
137          left, min, max, right = _range.GetRange()      if quantiles is not None:
138    
139            numGroups = len(quantiles[3])
140    
141            if numGroups != 0:
142    
143                adjusted = quantiles[0]
144    
145                ramp.SetNumGroups(numGroups)
146    
147                start, min, endMax, right = _range.GetRange()
148    
149                oldp = 0
150                i = 1
151                end = "]"
152    
153                for (q, p), prop in zip(quantiles[3], ramp):
154                    if i == numGroups:
155                        max = endMax
156                        end = right
157                    else:
158                        max = _list[q]
159    
160                    group = ClassGroupRange(Range((start, min, max, end)),
161                                            None, prop)
162        
163                    group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
164                                                    round(p*100, 2)))
165                    oldp = p
166                    start = "]"
167                    min = max
168                    clazz.AppendGroup(group)
169                    i += 1
170    
171        return (adjusted, clazz)
172    
173    def CalculateQuantiles(_list, percents, _range):
174        """Calculate quantiles for the given _list of percents from the
175        sorted list of values that are in range.
176                                                                                
177        This may not actually generate len(percents) quantiles if
178        many of the values that fall on quantile borders are the same.
179    
180        Returns a tuple of the form:
181            (adjusted, minIndex, maxIndex, [quantile_list])
182    
183        where adjusted is True if the the quantile percentages differ from
184        those supplied, minIndex is the index into _list where the
185        minimum value used is located, maxIndex is the index into _list
186        where the maximum value used is located, and quantile_list is a
187        list of tuples of the form: (list_index, quantile_percentage)
188    
189        Returns None, if no quantiles could be generated based on the
190        given range or input list.
191    
192        _list -- a sort list of values
193    
194        percents -- a sorted list of floats in the range 0.0-1.0 which
195                    represent the upper bound of each quantile
196    
197        _range -- a Range object
198        """
199    
200        quantiles = []
201        adjusted = False
202    
203        if len(percents) != 0:
204                                                                              
205            #
206            # find what part of the _list range covers
207            #
208            minIndex = -1
209            maxIndex = -2
210            for i in xrange(0, len(_list), 1):
211                if operator.contains(_range, _list[i]):
212                    minIndex = i
213                    break
214    
215            for i in xrange(len(_list)-1, -1, -1):
216                if operator.contains(_range, _list[i]):
217                    maxIndex = i
218                    break
219    
220            numValues = maxIndex - minIndex + 1
221    
222            if numValues > 0:
223    
         start = "["  
         oldp = 0  
         for (q, p), prop in zip(quantiles[1], ramp):  
             max = list[q]  
             group = ClassGroupRange(Range(start + str(min) + ";" +  
                                                   str(max) + "]"),  
                                     None, prop)  
   
             group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),  
                                             round(p*100, 2)))  
             oldp = p  
             start = "]"  
             min = max  
             clazz.AppendGroup(group)  
   
         return (quantiles[0], clazz)  
   
     def CalculateQuantiles(self, list, percents, _range):  
         """Calculate quantiles for the given list of percents from the  
         sorted list of values that are in range.  
                                                                                   
         percents is a sorted list of floats in the range 0.0-1.0  
   
         This may not actually generate numGroups quantiles if  
         many of the values that fall on quantile borders are the same.  
   
         Returns a tuple of the form: (adjusted, [quantile_list])  
   
         where adjusted is true if the the quantile percentages differ from  
         those supplied, and quantile_list is a list of tuples of the form:  
             (list_index, quantile_percentage)  
         """  
       
         quantiles = []  
                                                                                   
         adjusted = False  
         if len(percents) != 0:  
                                                                                 
             #print "list:", list  
             #print "percents:", percents  
             #print "numGroups:", numGroups  
       
224              #              #
225              # find what part of the list range covers              # build a list of unique indices into list of where each
226                # quantile *should* be. set adjusted if the resulting
227                # indices are different
228              #              #
229              minIndex = -1              quantiles = {}
230              maxIndex = -2              for p in percents:
231              for i in xrange(0, len(list), 1):                  index = min(minIndex + int(p*numValues)-1, maxIndex)
232                  if operator.contains(_range, list[i]):  
233                      minIndex = i                  adjusted = adjusted \
234                      break                      or quantiles.has_key(index) \
235                        or ((index - minIndex + 1) / float(numValues)) != p
236    
237              for i in xrange(len(list)-1, -1, -1):                  quantiles[index] = 0
                 if operator.contains(_range, list[i]):  
                     maxIndex = i  
                     break;  
   
             numValues = maxIndex - minIndex + 1  
             #print "minIndex:", minIndex, "maxIndex:", maxIndex  
             if minIndex <= maxIndex:  
238    
239                  #              quantiles = quantiles.keys()
240                  # build a list of unique indices into list of where each              quantiles.sort()
                 # quantile should be  
                 #  
                 quantiles = {}  
                 for p in percents:  
                     index = min(minIndex + int(p*numValues)-1, maxIndex)  
241    
242                      adjusted = adjusted \              #
243                          or quantiles.has_key(index) \              # the current quantile index must be strictly greater than
244                          or ((index+1) / float(numValues)) != p              # the lowerBound
245                #
246                lowerBound = minIndex - 1
247    
248                      quantiles[index] = 0              for qindex in xrange(len(quantiles)):
249                    if lowerBound >= maxIndex:
250                        # discard higher quantiles
251                        quantiles = quantiles[:qindex]
252                        break
253    
254                  quantiles = quantiles.keys()                  # lowerBound + 1 is always a valid index
                 quantiles.sort()  
255    
                 #print "quantiles:", quantiles  
       
256                  #                  #
257                  # the current quantile index must be strictly greater than                  # bump up the current quantile index to be a usable index
258                  # the lowerBound                  # if it currently falls below the lowerBound
259                  #                  #
260                  lowerBound = minIndex - 1                  if quantiles[qindex] <= lowerBound:
261                            quantiles[qindex] = lowerBound + 1
                 for qindex in range(len(quantiles)):  
                     if lowerBound >= maxIndex:  
                         # discard higher quantiles  
                         quantiles = quantiles[:qindex]  
                         break  
       
                     # lowerBound + 1 is always a valid index  
       
       
                     #  
                     # bump up the current quantile index to be a usable index  
                     # if it currently falls below the lowerBound  
                     #  
                     if quantiles[qindex] <= lowerBound:  
                         quantiles[qindex] = min(lowerBound + 1, maxIndex)  
           
                     listIndex = quantiles[qindex]  
                     value = list[quantiles[qindex]]  
       
                     #print "-----------------------------------"  
                     #print "listIndex:", listIndex  
                     #print "value:", value  
       
                     #  
                     # look for similar values around the quantile index  
                     #  
                   
                     #print "lowerBound:", lowerBound  
                     lindex = listIndex - 1  
                     lcount = 0  
                     while lindex > lowerBound:  
                         if value != list[lindex]: break  
                         lcount += 1  
                         lindex -= 1  
       
                     rindex = listIndex + 1  
                     rcount = 0  
                     while rindex < maxIndex + 1:  
                         if value != list[rindex]: break  
                         rcount += 1  
                         rindex += 1  
       
                     #print lcount, "(lcount)", rcount, "(rcount)"  
                     #print lindex, "(lindex)", rindex, "(rindex)"  
       
                     #  
                     # adjust the current quantile index based on how many  
                     # numbers in the list are the same as the current value  
                     #  
                     newIndex = listIndex  
                     if lcount == rcount:  
                         if lcount != 0:  
                             #  
                             # there are an equal number of numbers to the left  
                             # and right, try going to the left first unless  
                             # doing so creates an empty quantile.  
                             #  
                             if lindex != lowerBound:  
                                 newIndex = lindex  
                                 #quantiles[qindex] = lindex  
                             else:  
                                 newIndex = rindex - 1  
                                 #quantiles[qindex] = rindex - 1  
262            
263                      elif lcount < rcount:                  listIndex = quantiles[qindex]
264                          # there are fewer items to the left, so                  value = _list[listIndex]
265                          # try going to the left first unless  
266                    #
267                    # look for similar values around the quantile index
268                    #
269                    lindex = listIndex - 1
270                    while lindex > lowerBound and value == _list[lindex]:
271                        lindex -= 1
272                    lcount = (listIndex - 1) - lindex
273    
274                    rindex = listIndex + 1
275                    while rindex < maxIndex + 1 and value == _list[rindex]:
276                        rindex += 1
277                    rcount = (listIndex + 1) - rindex
278    
279                    #
280                    # adjust the current quantile index based on how many
281                    # numbers in the _list are the same as the current value
282                    #
283                    newIndex = listIndex
284                    if lcount == rcount:
285                        if lcount != 0:
286                            #
287                            # there are an equal number of numbers to the left
288                            # and right, try going to the left first unless
289                          # doing so creates an empty quantile.                          # doing so creates an empty quantile.
290                            #
291                          if lindex != lowerBound:                          if lindex != lowerBound:
292                              newIndex = lindex                              newIndex = lindex
                             #quantiles[qindex] = lindex  
293                          else:                          else:
294                              newIndex = rindex - 1                              newIndex = rindex - 1
295                              #quantiles[qindex] = rindex - 1  
296                        elif lcount < rcount:
297                      elif rcount < lcount:                      # there are fewer items to the left, so
298                          # there are fewer items to the right, so go to the right                      # try going to the left first unless
299                          #quantiles[qindex] = rindex - 1                      # doing so creates an empty quantile.
300                        if lindex != lowerBound:
301                            newIndex = lindex
302                        else:
303                          newIndex = rindex - 1                          newIndex = rindex - 1
304        
305                      quantiles[qindex] = newIndex                  elif rcount < lcount:
306                      lowerBound = quantiles[qindex]                      # there are fewer items to the right, so go to the right
307                            newIndex = rindex - 1
308                      #print "quantiles:", quantiles  
309                      #print "lowerBound:", lowerBound                  adjusted = adjusted or newIndex != listIndex
310            
311                  #print "================================="                  quantiles[qindex] = newIndex
312                  #print "quantiles:", quantiles                  lowerBound = quantiles[qindex]
313                  #print "qindex:", qindex  
314            if len(quantiles) == 0:
315          return (adjusted, [(q, (q+1) / float(numValues)) for q in quantiles])          return None
316        else:
317            return (adjusted, minIndex, maxIndex,
318                    [(q, (q - minIndex+1) / float(numValues)) \
319                     for q in quantiles])
320    
321  CLR  = 0  CLR  = 0
322  STEP = 1  STEP = 1

Legend:
Removed from v.886  
changed lines
  Added in v.1098

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26