/[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 1098 by jonathan, Fri May 30 06:27:44 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)
         if numGroups == 0: return clazz  
44    
45          ramp.SetNumGroups(numGroups)      for value, prop in zip(_list, ramp):
46            clazz.AppendGroup(ClassGroupSingleton(value, prop))
47    
48          for value, prop in zip(_list, ramp):      return clazz
             clazz.AppendGroup(ClassGroupSingleton(value, prop))  
49    
50          return clazz  def GenSingletons(min, max, numGroups, ramp):
51    
52      def GenSingletons(self, min, max, numGroups, ramp):      clazz = Classification()
53    
54          clazz = Classification()      #step = int((max - min) / float(numGroups))
55    
56          #step = int((max - min) / float(numGroups))      if numGroups > 0:
57    
58          if numGroups > 0:          step = int((max - min + 1) / float(numGroups))
59            cur_value = min
60    
61              step = int((max - min + 1) / float(numGroups))          ramp.SetNumGroups(numGroups)
             cur_value = min  
62    
63              ramp.SetNumGroups(numGroups)          for prop in ramp:
64                clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)
65                cur_value += step
66    
67              for prop in ramp:      return clazz
                 clazz.AppendGroup(ClassGroupSingleton(cur_value), prop)  
                 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).  
         """  
68    
69          clazz = Classification()  def GenUniformDistribution(min, max, numGroups,
70          if numGroups == 0: return clazz                             ramp, intStep = False):
71        """Generate a classification with numGroups range groups
72        each with the same interval.
73    
74          ramp.SetNumGroups(numGroups)      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 = (max - min) / float(numGroups)      clazz = Classification()
81        if numGroups == 0: return clazz
82    
83          if intStep:      ramp.SetNumGroups(numGroups)
             step = int(step)  
84    
85          cur_min = min      step = (max - min) / float(numGroups)
         cur_max = cur_min + step  
86    
87          i = 0      if intStep:
88          end = "["          step = int(step)
         for prop in ramp:  
89    
90              if i == (numGroups - 1):      cur_min = min
91                  cur_max = max      cur_max = cur_min + step
                 end = "]"  
92    
93        i = 0
94        end = "["
95        for prop in ramp:
96    
97              # this check guards against rounding issues          if i == (numGroups - 1):
98              if cur_min != cur_max:              cur_max = max
99                  range = Range(("[", cur_min, cur_max, end))              end = "]"
                 clazz.AppendGroup(ClassGroupRange(range, None, prop))  
100    
             cur_min = cur_max  
             cur_max += step  
             i += 1  
101    
102          return clazz          # this check guards against rounding issues
103            if cur_min != cur_max:
104                range = Range(("[", cur_min, cur_max, end))
105                clazz.AppendGroup(ClassGroupRange(range, None, prop))
106    
107            cur_min = cur_max
108            cur_max += step
109            i += 1
110    
111      def GenQuantiles(self, _list, percents, ramp, _range):      return clazz
         """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.  
112    
         Returns a tuple (adjusted, Classification) where adjusted is  
         True if the Classification does not exactly represent the given  
         range, or if the Classification is empty.  
113    
114          _list -- a sort list of values  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          percents -- a sorted list of floats in the range 0.0-1.0 which      Returns a tuple (adjusted, Classification) where adjusted is
120                      represent the upper bound of each quantile      True if the Classification does not exactly represent the given
121        range, or if the Classification is empty.
122    
123          ramp -- an object which implements the CustomRamp interface      _list -- a sort list of values
124    
125          _range -- a Range object      percents -- a sorted list of floats in the range 0.0-1.0 which
126          """                  represent the upper bound of each quantile
127    
128          clazz = Classification()      ramp -- an object which implements the CustomRamp interface
         quantiles = self.CalculateQuantiles(_list, percents, _range)  
         adjusted = True  
129    
130          if quantiles is not None:      _range -- a Range object
131        """
132    
133              numGroups = len(quantiles[3])      clazz = Classification()
134        quantiles = CalculateQuantiles(_list, percents, _range)
135        adjusted = True
136    
137              if numGroups != 0:      if quantiles is not None:
138    
139                  adjusted = quantiles[0]          numGroups = len(quantiles[3])
140    
141                  ramp.SetNumGroups(numGroups)          if numGroups != 0:
142    
143                  start, min, endMax, right = _range.GetRange()              adjusted = quantiles[0]
144    
145                  oldp = 0              ramp.SetNumGroups(numGroups)
                 i = 1  
                 end = "]"  
146    
147                  for (q, p), prop in zip(quantiles[3], ramp):              start, min, endMax, right = _range.GetRange()
                     if i == numGroups:  
                         max = endMax  
                         end = right  
                     else:  
                         max = _list[q]  
148    
149                      group = ClassGroupRange(Range((start, min, max, end)),              oldp = 0
150                                              None, prop)              i = 1
151                        end = "]"
152                      group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),  
153                                                      round(p*100, 2)))              for (q, p), prop in zip(quantiles[3], ramp):
154                      oldp = p                  if i == numGroups:
155                      start = "]"                      max = endMax
156                      min = max                      end = right
157                      clazz.AppendGroup(group)                  else:
158                      i += 1                      max = _list[q]
159    
160          return (adjusted, clazz)                  group = ClassGroupRange(Range((start, min, max, end)),
161                                            None, prop)
162      def CalculateQuantiles(self, _list, percents, _range):      
163          """Calculate quantiles for the given _list of percents from the                  group.SetLabel("%s%% - %s%%" % (round(oldp*100, 2),
164          sorted list of values that are in range.                                                  round(p*100, 2)))
165                                                                                                    oldp = p
166          This may not actually generate len(percents) quantiles if                  start = "]"
167          many of the values that fall on quantile borders are the same.                  min = max
168                    clazz.AppendGroup(group)
169          Returns a tuple of the form:                  i += 1
170              (adjusted, minIndex, maxIndex, [quantile_list])  
171        return (adjusted, clazz)
172          where adjusted is True if the the quantile percentages differ from  
173          those supplied, minIndex is the index into _list where the  def CalculateQuantiles(_list, percents, _range):
174          minimum value used is located, maxIndex is the index into _list      """Calculate quantiles for the given _list of percents from the
175          where the maximum value used is located, and quantile_list is a      sorted list of values that are in range.
176          list of tuples of the form: (list_index, quantile_percentage)                                                                              
177        This may not actually generate len(percents) quantiles if
178          Returns None, if no quantiles could be generated based on the      many of the values that fall on quantile borders are the same.
179          given range or input list.  
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          _list -- a sort list of values      _range -- a Range object
198        """
199    
200          percents -- a sorted list of floats in the range 0.0-1.0 which      quantiles = []
201                      represent the upper bound of each quantile      adjusted = False
202    
203          _range -- a Range object      if len(percents) != 0:
204          """                                                                            
205                #
206          quantiles = []          # find what part of the _list range covers
207          adjusted = False          #
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    
         if len(percents) != 0:  
                                                                                 
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
238                  if operator.contains(_range, _list[i]):  
239                      maxIndex = i              quantiles = quantiles.keys()
240                      break              quantiles.sort()
241    
242                #
243                # the current quantile index must be strictly greater than
244                # the lowerBound
245                #
246                lowerBound = minIndex - 1
247    
248              numValues = maxIndex - minIndex + 1              for qindex in xrange(len(quantiles)):
249                    if lowerBound >= maxIndex:
250                        # discard higher quantiles
251                        quantiles = quantiles[:qindex]
252                        break
253    
254              if numValues > 0:                  # lowerBound + 1 is always a valid index
255    
256                  #                  #
257                  # build a list of unique indices into list of where each                  # bump up the current quantile index to be a usable index
258                  # quantile *should* be. set adjusted if the resulting                  # if it currently falls below the lowerBound
                 # indices are different  
259                  #                  #
260                  quantiles = {}                  if quantiles[qindex] <= lowerBound:
261                  for p in percents:                      quantiles[qindex] = lowerBound + 1
262                      index = min(minIndex + int(p*numValues)-1, maxIndex)      
263                    listIndex = quantiles[qindex]
264                      adjusted = adjusted \                  value = _list[listIndex]
                         or quantiles.has_key(index) \  
                         or ((index - minIndex + 1) / float(numValues)) != p  
   
                     quantiles[index] = 0  
265    
266                  quantiles = quantiles.keys()                  #
267                  quantiles.sort()                  # 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                  # the current quantile index must be strictly greater than                  # adjust the current quantile index based on how many
281                  # the lowerBound                  # numbers in the _list are the same as the current value
282                  #                  #
283                  lowerBound = minIndex - 1                  newIndex = listIndex
284                        if lcount == rcount:
285                  for qindex in xrange(len(quantiles)):                      if lcount != 0:
286                      if lowerBound >= maxIndex:                          #
287                          # discard higher quantiles                          # there are an equal number of numbers to the left
288                          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  
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
293                          else:                          else:
294                              newIndex = rindex - 1                              newIndex = rindex - 1
295        
296                      elif rcount < lcount:                  elif lcount < rcount:
297                          # there are fewer items to the right, so go to the right                      # there are fewer items to the left, so
298                        # try going to the left first unless
299                        # doing so creates an empty quantile.
300                        if lindex != lowerBound:
301                            newIndex = lindex
302                        else:
303                          newIndex = rindex - 1                          newIndex = rindex - 1
       
                     adjusted = adjusted or newIndex != listIndex  
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          #  
309          # since quantiles is only set if the code is at least a little                  adjusted = adjusted or newIndex != listIndex
310          # successful, an empty list will be generated in the case that  
311          # we fail to get to the real body of the algorithm                  quantiles[qindex] = newIndex
312          #                  lowerBound = quantiles[qindex]
313          if len(quantiles) == 0:  
314              return None      if len(quantiles) == 0:
315          else:          return None
316              return (adjusted, minIndex, maxIndex,      else:
317                      [(q, (q - minIndex+1) / float(numValues)) \          return (adjusted, minIndex, maxIndex,
318                       for q in quantiles])                  [(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.986  
changed lines
  Added in v.1098

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26