/[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 986 by tkoester, Thu May 22 16:41:10 2003 UTC revision 1128 by tkoester, Tue Jun 3 17:00:47 2003 UTC
# Line 6  Line 6 
6  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
7    
8  """  """
9  ClassGenerator  Functions to generate Classifications
10  """  """
11    
12  __version__ = "$Revision$"  __version__ = "$Revision$"
# Line 20  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      def GenSingletonsFromList(self, _list, numGroups, ramp):      The resulting classification will consist of at most 'numGroups'
27          """Generate a new classification consisting solely of singletons.      groups whose group properties ramp between 'prop1' and 'prop2'. There
28        could be fewer groups if '_list' contains fewer that 'numGroups' items.
29    
30          The resulting classification will consist of at most 'numGroups'      _list -- any object that implements the iterator interface
         groups whose group properties ramp between 'prop1' and 'prop2'. There  
         could be fewer groups if '_list' contains fewer that 'numGroups' items.  
31    
32          _list -- any object that implements the iterator interface      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          numGroups -- how many groups to generate. This can not be      ramp -- an object which implements the CustomRamp interface
38                       determined while the classification is being      """
                      generated because the stepping values must  
                      be precalculated to ramp between prop1 and prop2.  
39    
40          ramp -- an object which implements the CustomRamp interface      clazz = Classification()
41          """      if numGroups == 0: return clazz
42    
43          clazz = Classification()      ramp.SetNumGroups(numGroups)
44          if numGroups == 0: return clazz  
45        for value, prop in zip(_list, ramp):
46            clazz.AppendGroup(ClassGroupSingleton(value, prop))
47    
48        return clazz
49    
50    def GenSingletons(min, max, numGroups, ramp):
51    
52        clazz = Classification()
53    
54        #step = int((max - min) / float(numGroups))
55    
56        if numGroups > 0:
57    
58            step = int((max - min + 1) / float(numGroups))
59            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 GenUniformDistribution(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(("[", cur_min, cur_max, end))      range, or if the Classification is empty.
                 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          """Generates a Classification which has groups of ranges that      """
         represent quantiles of _list at the percentages given in percents.  
         Only the values that fall within _range are considered.  
132    
133          Returns a tuple (adjusted, Classification) where adjusted is      clazz = Classification()
134          True if the Classification does not exactly represent the given      quantiles = CalculateQuantiles(_list, percents, _range)
135          range, or if the Classification is empty.      adjusted = True
136    
137          _list -- a sort list of values      if quantiles is not None:
138    
139          percents -- a sorted list of floats in the range 0.0-1.0 which          numGroups = len(quantiles[3])
                     represent the upper bound of each quantile  
140    
141          ramp -- an object which implements the CustomRamp interface          if numGroups != 0:
142    
143          _range -- a Range object              adjusted = quantiles[0]
         """  
144    
145          clazz = Classification()              ramp.SetNumGroups(numGroups)
         quantiles = self.CalculateQuantiles(_list, percents, _range)  
         adjusted = True  
146    
147          if quantiles is not None:              start, min, endMax, right = _range.GetRange()
148    
149              numGroups = len(quantiles[3])              oldp = 0
150                i = 1
151                end = "]"
152    
153              if numGroups != 0:              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                  adjusted = quantiles[0]                  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                  ramp.SetNumGroups(numGroups)      return (adjusted, clazz)
172    
                 start, min, endMax, right = _range.GetRange()  
173    
174                  oldp = 0  def GenQuantiles0(_list, percents, ramp, _range):
175                  i = 1      """Same as GenQuantiles, but the first class won't be added to
176                  end = "]"      the classification.
177    
178                  for (q, p), prop in zip(quantiles[3], ramp):      Returns a tuple (adjusted, Classification, upper_class0) where
179                      if i == numGroups:      upper_class0 is the highest value inside the first class.
                         max = endMax  
                         end = right  
                     else:  
                         max = _list[q]  
180    
181                      group = ClassGroupRange(Range((start, min, max, end)),      _list -- a sort list of values
                                             None, prop)  
           
                     group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),  
                                                     round(p*100, 2)))  
                     oldp = p  
                     start = "]"  
                     min = max  
                     clazz.AppendGroup(group)  
                     i += 1  
   
         return (adjusted, 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.  
                                                                                   
         This may not actually generate len(percents) quantiles if  
         many of the values that fall on quantile borders are the same.  
   
         Returns a tuple of the form:  
             (adjusted, minIndex, maxIndex, [quantile_list])  
   
         where adjusted is True if the the quantile percentages differ from  
         those supplied, minIndex is the index into _list where the  
         minimum value used is located, maxIndex is the index into _list  
         where the maximum value used is located, and quantile_list is a  
         list of tuples of the form: (list_index, quantile_percentage)  
   
         Returns None, if no quantiles could be generated based on the  
         given range or input list.  
182    
183          _list -- a sort list of values      percents -- a sorted list of floats in the range 0.0-1.0 which
184                    represent the upper bound of each quantile
185    
186          percents -- a sorted list of floats in the range 0.0-1.0 which      ramp -- an object which implements the CustomRamp interface
                     represent the upper bound of each quantile  
187    
188          _range -- a Range object      _range -- a Range object
189          """      """
190        
191          quantiles = []      clazz = Classification()
192          adjusted = False      quantiles = CalculateQuantiles(_list, percents, _range)
193        adjusted = True
194    
195        if quantiles is not None:
196    
197            numGroups = len(quantiles[3]) - 1
198    
199            if numGroups > 0:
200                adjusted = quantiles[0]
201    
202                ramp.SetNumGroups(numGroups)
203    
204                start, min, endMax, right = _range.GetRange()
205    
206                class0 = quantiles[3][0]
207                min = _list[class0[0]]
208                oldp = class0[1]
209                i = 1
210                end = "]"
211    
212                for (q, p), prop in zip(quantiles[3][1:], ramp):
213                    if i == numGroups:
214                        max = endMax
215                        end = right
216                    else:
217                        max = _list[q]
218    
219                    group = ClassGroupRange(Range((start, min, max, end)),
220                                            None, prop)
221        
222                    group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
223                                                    round(p*100, 2)))
224                    oldp = p
225                    start = "]"
226                    min = max
227                    clazz.AppendGroup(group)
228                    i += 1
229    
230        return (adjusted, clazz, _list[class0[0]])
231    
232    
233    def CalculateQuantiles(_list, percents, _range):
234        """Calculate quantiles for the given _list of percents from the
235        sorted list of values that are in range.
236                                                                                
237        This may not actually generate len(percents) quantiles if
238        many of the values that fall on quantile borders are the same.
239    
240        Returns a tuple of the form:
241            (adjusted, minIndex, maxIndex, [quantile_list])
242    
243        where adjusted is True if the the quantile percentages differ from
244        those supplied, minIndex is the index into _list where the
245        minimum value used is located, maxIndex is the index into _list
246        where the maximum value used is located, and quantile_list is a
247        list of tuples of the form: (list_index, quantile_percentage)
248    
249        Returns None, if no quantiles could be generated based on the
250        given range or input list.
251    
252        _list -- a sort list of values
253    
254        percents -- a sorted list of floats in the range 0.0-1.0 which
255                    represent the upper bound of each quantile
256    
257        _range -- a Range object
258        """
259    
260        quantiles = []
261        adjusted = False
262    
263        if len(percents) != 0:
264                                                                              
265            #
266            # find what part of the _list range covers
267            #
268            minIndex = -1
269            maxIndex = -2
270            for i in xrange(0, len(_list), 1):
271                if operator.contains(_range, _list[i]):
272                    minIndex = i
273                    break
274    
275            for i in xrange(len(_list)-1, -1, -1):
276                if operator.contains(_range, _list[i]):
277                    maxIndex = i
278                    break
279    
280            numValues = maxIndex - minIndex + 1
281    
282            if numValues > 0:
283    
         if len(percents) != 0:  
                                                                                 
284              #              #
285              # find what part of the _list range covers              # build a list of unique indices into list of where each
286                # quantile *should* be. set adjusted if the resulting
287                # indices are different
288              #              #
289              minIndex = -1              quantiles = {}
290              maxIndex = -2              for p in percents:
291              for i in xrange(0, len(_list), 1):                  index = min(minIndex + int(p*numValues)-1, maxIndex)
292                  if operator.contains(_range, _list[i]):  
293                      minIndex = i                  adjusted = adjusted \
294                      break                      or quantiles.has_key(index) \
295                        or ((index - minIndex + 1) / float(numValues)) != p
296    
297              for i in xrange(len(_list)-1, -1, -1):                  quantiles[index] = 0
298                  if operator.contains(_range, _list[i]):  
299                      maxIndex = i              quantiles = quantiles.keys()
300                      break              quantiles.sort()
301    
302                #
303                # the current quantile index must be strictly greater than
304                # the lowerBound
305                #
306                lowerBound = minIndex - 1
307    
308              numValues = maxIndex - minIndex + 1              for qindex in xrange(len(quantiles)):
309                    if lowerBound >= maxIndex:
310                        # discard higher quantiles
311                        quantiles = quantiles[:qindex]
312                        break
313    
314              if numValues > 0:                  # lowerBound + 1 is always a valid index
315    
316                  #                  #
317                  # build a list of unique indices into list of where each                  # bump up the current quantile index to be a usable index
318                  # quantile *should* be. set adjusted if the resulting                  # if it currently falls below the lowerBound
                 # indices are different  
319                  #                  #
320                  quantiles = {}                  if quantiles[qindex] <= lowerBound:
321                  for p in percents:                      quantiles[qindex] = lowerBound + 1
322                      index = min(minIndex + int(p*numValues)-1, maxIndex)      
323                    listIndex = quantiles[qindex]
324                      adjusted = adjusted \                  value = _list[listIndex]
                         or quantiles.has_key(index) \  
                         or ((index - minIndex + 1) / float(numValues)) != p  
   
                     quantiles[index] = 0  
325    
326                  quantiles = quantiles.keys()                  #
327                  quantiles.sort()                  # look for similar values around the quantile index
328                    #
329                    lindex = listIndex - 1
330                    while lindex > lowerBound and value == _list[lindex]:
331                        lindex -= 1
332                    lcount = (listIndex - 1) - lindex
333    
334                    rindex = listIndex + 1
335                    while rindex < maxIndex + 1 and value == _list[rindex]:
336                        rindex += 1
337                    rcount = (listIndex + 1) - rindex
338    
339                  #                  #
340                  # the current quantile index must be strictly greater than                  # adjust the current quantile index based on how many
341                  # the lowerBound                  # numbers in the _list are the same as the current value
342                  #                  #
343                  lowerBound = minIndex - 1                  newIndex = listIndex
344                        if lcount == rcount:
345                  for qindex in xrange(len(quantiles)):                      if lcount != 0:
346                      if lowerBound >= maxIndex:                          #
347                          # discard higher quantiles                          # there are an equal number of numbers to the left
348                          quantiles = quantiles[:qindex]                          # and right, try going to the left first unless
                         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] = lowerBound + 1  
           
                     listIndex = quantiles[qindex]  
                     value = _list[listIndex]  
       
                     #  
                     # look for similar values around the quantile index  
                     #  
                     lindex = listIndex - 1  
                     while lindex > lowerBound and value == _list[lindex]:  
                         lindex -= 1  
                     lcount = (listIndex - 1) - lindex  
       
                     rindex = listIndex + 1  
                     while rindex < maxIndex + 1 and value == _list[rindex]:  
                         rindex += 1  
                     rcount = (listIndex + 1) - 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  
                             else:  
                                 newIndex = rindex - 1  
       
                     elif lcount < rcount:  
                         # there are fewer items to the left, so  
                         # try going to the left first unless  
349                          # doing so creates an empty quantile.                          # doing so creates an empty quantile.
350                            #
351                          if lindex != lowerBound:                          if lindex != lowerBound:
352                              newIndex = lindex                              newIndex = lindex
353                          else:                          else:
354                              newIndex = rindex - 1                              newIndex = rindex - 1
355        
356                      elif rcount < lcount:                  elif lcount < rcount:
357                          # there are fewer items to the right, so go to the right                      # there are fewer items to the left, so
358                        # try going to the left first unless
359                        # doing so creates an empty quantile.
360                        if lindex != lowerBound:
361                            newIndex = lindex
362                        else:
363                          newIndex = rindex - 1                          newIndex = rindex - 1
       
                     adjusted = adjusted or newIndex != listIndex  
364    
365                      quantiles[qindex] = newIndex                  elif rcount < lcount:
366                      lowerBound = quantiles[qindex]                      # there are fewer items to the right, so go to the right
367                            newIndex = rindex - 1
368          #  
369          # since quantiles is only set if the code is at least a little                  adjusted = adjusted or newIndex != listIndex
370          # successful, an empty list will be generated in the case that  
371          # we fail to get to the real body of the algorithm                  quantiles[qindex] = newIndex
372          #                  lowerBound = quantiles[qindex]
373          if len(quantiles) == 0:  
374              return None      if len(quantiles) == 0:
375          else:          return None
376              return (adjusted, minIndex, maxIndex,      else:
377                      [(q, (q - minIndex+1) / float(numValues)) \          return (adjusted, minIndex, maxIndex,
378                       for q in quantiles])                  [(q, (q - minIndex+1) / float(numValues)) \
379                     for q in quantiles])
380    
381  CLR  = 0  CLR  = 0
382  STEP = 1  STEP = 1

Legend:
Removed from v.986  
changed lines
  Added in v.1128

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26