~ubuntu-branches/ubuntu/natty/pytables/natty-updates

« back to all changes in this revision

Viewing changes to tables/IndexArray.py

  • Committer: Bazaar Package Importer
  • Author(s): Alexandre Fayolle
  • Date: 2006-06-28 10:45:03 UTC
  • mfrom: (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 5.
  • Revision ID: james.westby@ubuntu.com-20060628104503-cc251q5o5j3e2k10
  * Fixed call to pyversions in debian/rules which failed on recent versions 
    of pyversions
  * Fixed clean rule in debian/rules which left the stamp files behind
  * Acknowledge NMU
  * Added Alexandre Fayolle to uploaders

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
#
3
3
#       License: BSD
4
4
#       Created: June 02, 2004
5
 
#       Author:  Francesc Alted - falted@pytables.org
 
5
#       Author:  Francesc Altet - faltet@carabos.com
6
6
#
7
 
#       $Source: /cvsroot/pytables/pytables/tables/IndexArray.py,v $
8
 
#       $Id: IndexArray.py,v 1.10.2.1 2004/11/10 13:55:48 falted Exp $
 
7
#       $Id: IndexArray.py 1496 2006-03-13 09:48:13Z faltet $
9
8
#
10
9
########################################################################
11
10
 
27
26
 
28
27
"""
29
28
 
30
 
__version__ = "$Revision: 1.10.2.1 $"
31
 
# default version for IndexARRAY objects
32
 
obversion = "1.0"    # initial version
 
29
import warnings
 
30
import sys
33
31
 
34
 
import types, warnings, sys
35
 
from EArray import EArray
36
 
from VLArray import Atom, StringAtom
37
 
import hdf5Extension
38
32
import numarray
39
33
import numarray.strings as strings
40
34
import numarray.records as records
41
35
 
42
 
def calcChunksize(expectedrows, testmode=0):
 
36
import tables.hdf5Extension as hdf5Extension
 
37
from tables.Atom import Atom, StringAtom
 
38
from tables.EArray import EArray
 
39
 
 
40
 
 
41
__version__ = "$Revision: 1496 $"
 
42
 
 
43
# default version for IndexARRAY objects
 
44
obversion = "1.0"    # initial version
 
45
 
 
46
# The minimum row number in a column that can be indexed in tests
 
47
minRowIndex = 10
 
48
 
 
49
 
 
50
def calcChunksize(expectedrows, testmode=False):
43
51
    """Calculate the HDF5 chunk size for index and sorted arrays.
44
52
 
45
53
    The logic to do that is based purely in experiments playing with
50
58
 
51
59
    """
52
60
 
53
 
    expKrows = expectedrows / 1000000.  # Multiples of one million
54
 
 
55
61
    if testmode:
56
 
        if expKrows < 0.0001: # expected rows < 1 hundred
57
 
            nelemslice = 10  # > 1/100th
58
 
            chunksize = 10
59
 
        elif expKrows < 0.001: # expected rows < 1 thousand
60
 
            nelemslice = 100  # > 1/10th
 
62
        if expectedrows < minRowIndex*10:
 
63
            nelemslice = minRowIndex
 
64
            chunksize = minRowIndex
 
65
        elif expectedrows < minRowIndex*100:
 
66
            nelemslice = 100
61
67
            chunksize = 50
62
 
        elif expKrows <= 0.01: # expected rows < 10 thousand
63
 
            nelemslice = 1000  # > 1/100th
 
68
        elif expectedrows <= minRowIndex*1000:
 
69
            nelemslice = 1000
64
70
            chunksize = 600
65
71
        else:
66
72
            raise ValueError, \
67
 
                  "expected rows cannot be larger than 10000 in test mode"
 
73
                  "expected rows cannot be larger than %s in test mode" % minRowIndex*1000
 
74
        #print "nelemslice, chunksize:", (nelemslice, chunksize)
68
75
        return (nelemslice, chunksize)
69
76
 
 
77
    expKrows = expectedrows / 1000000.  # Multiples of one million
 
78
 
70
79
    # expKrows < 0.01 is to few for indexing to represent a significant gain
71
80
    # (that has been checked experimentally)
72
81
#     if expKrows < 0.01: # expected rows < 10 thousand
75
84
    if expKrows < 0.1: # expected rows < 100 thousand
76
85
        nelemslice = 5000  # (best experimental)
77
86
        chunksize = 1000
78
 
#         nelemslice = 5*1024 
 
87
#         nelemslice = 5*1024
79
88
#         chunksize = 1024
80
89
    elif expKrows < 1: # expected rows < 1 milion
81
90
        nelemslice = 20000  # (best experimental)
82
91
        chunksize = 2000   # (best experimental)
83
 
#         nelemslice = 5*1024 
 
92
#         nelemslice = 5*1024
84
93
#         chunksize = 1024
85
94
#         chunksize = 2048  # (best experimental)
86
95
#         nelemslice = 10*chunksize   # (best experimental)
87
96
       #chunksize = 2048
88
 
       #nelemslice = 10*chunksize 
 
97
       #nelemslice = 10*chunksize
89
98
    elif expKrows < 10:  # expected rows < 10 milion
90
99
        #nelemslice = 500000  # > 1/20th (best for best case)
91
100
        #chunksize = 5000  # Experimental (best for best case)
93
102
        chunksize = 2000  # best for worst case (experimental)
94
103
        #chunksize = 4096  # (best experimental)
95
104
        #nelemslice = 10*chunksize   # (best experimental)
96
 
#         nelemslice = 20*4096 
 
105
#         nelemslice = 20*4096
97
106
#         chunksize = 4096
98
107
    elif expKrows < 100: # expected rows < 100 milions
99
108
        nelemslice = 100000
100
109
        chunksize = 5000
101
 
#         nelemslice = 20*4096 
 
110
#         nelemslice = 20*4096
102
111
#         chunksize = 4096
103
112
    elif expKrows < 1000: # expected rows < 1000 millions
104
113
        nelemslice = 200000 # Experimental (best)
114
123
    #print "nelemslice, chunksize:", (nelemslice, chunksize)
115
124
    return (nelemslice, chunksize)
116
125
 
117
 
class IndexArray(EArray, hdf5Extension.IndexArray, object):
 
126
 
 
127
class IndexArray(hdf5Extension.IndexArray, EArray):
 
128
 
118
129
    """Represent the index (sorted or reverse index) dataset in HDF5 file.
119
130
 
120
131
    All Numeric and numarray typecodes are supported except for complex
127
138
        iterrows(start, stop, step)
128
139
        append(object)
129
140
 
130
 
        
 
141
 
131
142
    Instance variables:
132
143
 
133
144
      Common to all EArray's:
143
154
        extdim -- The enlargeable dimension (always the first, or 0).
144
155
        nrows -- The number of slices in index.
145
156
        nelemslice -- The number of elements per slice.
146
 
        chunksize -- The HDF5 chunksize for the slice dimension (the 1).
147
 
            
 
157
        chunksize -- The HDF5 chunksize for the slice dimension (the second).
148
158
 
149
159
    """
150
 
    
151
 
    def __init__(self, parent = None, atom = None, title = "",
152
 
                 filters = None, expectedrows = 1000000,
153
 
                 testmode=0):
 
160
 
 
161
    _c_classId = "INDEXARRAY"
 
162
 
 
163
 
 
164
    def __init__(self, parentNode, name,
 
165
                 atom=None, title="",
 
166
                 filters=None,
 
167
                 testmode=False,
 
168
                 expectedrows=0):
154
169
        """Create an IndexArray instance.
155
170
 
156
171
        Keyword arguments:
157
172
 
158
 
        parent -- The Index class from which this object will hang off
159
 
 
160
173
        atom -- An Atom object representing the shape, type and flavor
161
174
            of the atomic objects to be saved. Only scalar atoms are
162
175
            supported.
163
 
        
 
176
 
164
177
        title -- Sets a TITLE attribute on the array entity.
165
178
 
166
179
        filters -- An instance of the Filters class that provides
168
181
            during the life of this object.
169
182
 
170
183
        expectedrows -- Represents an user estimate about the number
171
 
            of elements to index. If not provided, the default
172
 
            value is 1000000 slices.
 
184
            of elements to index.
173
185
 
174
186
        """
175
 
        self._v_parent = parent
176
 
        self._v_new_title = title
177
 
        self._v_new_filters = filters
178
 
        self._v_expectedrows = expectedrows
 
187
 
179
188
        self.testmode = testmode
180
 
        self.flavor = "NumArray"  # Needed by Array methods
181
 
        # Check if we have to create a new object or read their contents
182
 
        # from disk
 
189
        """Enables test mode for index chunk size calculation."""
 
190
        self.nelemslice = None
 
191
        """The number of elements per slice."""
 
192
        self.chunksize = None
 
193
        """The HDF5 chunksize for the slice dimension (the second)."""
 
194
 
 
195
        # Compute the optimum number of slices and chunk sizes
 
196
        # for newly created index arrays.
183
197
        if atom is not None:
184
 
            self._v_new = 1
185
 
            self.atom = atom
186
 
        else:
187
 
            self._v_new = 0
188
 
            
189
 
    def _create(self):
190
 
        """Save a fresh array (i.e., not present on HDF5 file)."""
191
 
        global obversion
192
 
 
193
 
        assert isinstance(self.atom, Atom), "The object passed to the IndexArray constructor must be a descendent of the Atom class."
194
 
        assert self.atom.shape == 1, "Only scalar columns can be indexed."
195
 
        # Version, type, shape, flavor, byteorder
196
 
        self._v_version = obversion
197
 
        self.type = self.atom.type
198
 
        if self.type == "CharType" or isinstance(self.type, records.Char):
199
 
            self.byteorder = "non-relevant"
200
 
        else:
201
 
            # Only support for creating objects in system byteorder
202
 
            self.byteorder  = sys.byteorder
203
 
        # Compute the optimal chunksize
204
 
        (self.nelemslice, self.chunksize) = \
205
 
                          calcChunksize(self._v_expectedrows,
206
 
                                        testmode=self.testmode)
207
 
        # The next is needed by hdf5Extension.Array._createEArray
208
 
        self._v_chunksize = (1, self.chunksize)
209
 
        self.nrows = 0   # No rows initially
210
 
        self.itemsize = self.atom.itemsize
211
 
        self.rowsize = self.atom.atomsize() * self.nelemslice
 
198
            (self.nelemslice, self.chunksize) = (
 
199
                calcChunksize(expectedrows, testmode))
 
200
 
 
201
        # Index creation is never logged.
 
202
        super(IndexArray, self).__init__(
 
203
            parentNode, name, atom, title, filters, expectedrows, log=False)
 
204
 
 
205
 
 
206
    def _g_create(self):
 
207
        assert self.atom.shape == (0, 1), "only scalar columns can be indexed"
 
208
        objectId = super(IndexArray, self)._g_create()
 
209
        assert self.extdim == 0, "computed extendable dimension is wrong"
 
210
        assert self.shape == (0, self.nelemslice), "invalid shape"
 
211
        assert self._v_chunksize == (1, self.chunksize), "invalid chunk size"
 
212
        return objectId
 
213
 
 
214
 
 
215
    def _calcTuplesAndChunks(self, atom, extdim, expectedrows, compress):
 
216
        return (0, (1, self.chunksize))  # (_v_maxTuples, _v_chunksize)
 
217
 
 
218
 
 
219
    def _createEArray(self, title):
 
220
        # The shape of the index array needs to be fixed before creating it.
212
221
        self.shape = (0, self.nelemslice)
213
 
        
214
 
        # extdim computation
215
 
        self.extdim = 0
216
 
        # Compute the optimal maxTuples
217
 
        # Ten chunks for each buffer would be enough for IndexArray objects
218
 
        # This is really necessary??
219
 
        self._v_maxTuples = 10  
220
 
        # Create the IndexArray
221
 
        self._createEArray("INDEXARRAY", self._v_new_title)
222
 
            
223
 
    def _open(self):
224
 
        """Get the metadata info for an array in file."""
225
 
        (self.type, self.shape, self.itemsize,
226
 
         self.byteorder, chunksizes) = self._openArray()
227
 
        self.chunksize = chunksizes[1]  # Get the second dim
228
 
        # Post-condition
229
 
        assert self.extdim == 0, "extdim != 0: this should never happen!"
230
 
        self.nelemslice = self.shape[1] 
231
 
        # Create the atom instance. Not for strings yet!
232
 
        if str(self.type) == "CharType":
233
 
            self.atom = StringAtom(shape=1, length=self.itemsize)
234
 
        else:
235
 
            self.atom = Atom(dtype=self.type, shape=1)
236
 
        # Compute the rowsize for each element
237
 
        self.rowsize = self.atom.atomsize() * self.nelemslice
238
 
        # nrows in this instance
239
 
        self.nrows = self.shape[0]
240
 
        # Compute the optimal maxTuples
241
 
        # Ten chunks for each buffer would be enough for IndexArray objects
242
 
        # This is really necessary??
243
 
        self._v_maxTuples = 10  
 
222
        super(IndexArray, self)._createEArray(title)
 
223
 
 
224
 
 
225
    def _g_postInitHook(self):
 
226
        # Set ``nelemslice`` and ``chunksize`` when opening an existing node;
 
227
        # otherwise, they are already set.
 
228
        if not self._v_new:
 
229
            self.nelemslice = self.shape[1]
 
230
            self.chunksize = self._v_chunksize[1]
 
231
        super(IndexArray, self)._g_postInitHook()
 
232
 
244
233
 
245
234
    def append(self, arr):
246
235
        """Append the object to this (enlargeable) object"""
247
236
        arr.shape = (1, arr.shape[0])
248
237
        self._append(arr)
249
238
 
 
239
 
250
240
    # This is coded in pyrex as well, but the improvement in speed is very
251
241
    # little. So, it's better to let _searchBin live here.
252
242
    def _searchBin(self, nrow, item):
253
243
        nelemslice = self.shape[1]
254
 
        hi = nelemslice   
 
244
        hi = nelemslice
255
245
        item1, item2 = item
256
246
        item1done = 0; item2done = 0
257
247
        chunksize = self.chunksize # Number of elements/chunksize
268
258
        if 0 <= result2 < chunksize:
269
259
            item2done = 1
270
260
        if item1done and item2done:
271
 
            # print "done 1"
272
261
            return (result1, result2, niter)
273
262
 
274
263
        # Then, look for items at the end of the sorted slice
286
275
                item2done = 1
287
276
                result2 = hi - chunksize + result2
288
277
        if item1done and item2done:
289
 
            # print "done 2"
290
278
            return (result1, result2, niter)
291
 
    
 
279
 
292
280
        # Finally, do a lookup for item1 and item2 if they were not found
293
281
        # Lookup in the middle of slice for item1
294
282
        if not item1done:
306
294
                    break
307
295
                else:
308
296
                    hi = result1        # one chunk to the left
309
 
                    lo = hi - chunksize  
 
297
                    lo = hi - chunksize
310
298
            result1 = tmpresult1
311
299
        # Lookup in the middle of slice for item1
312
300
        if not item2done:
329
317
            niter = niter + iter
330
318
        return (result1, result2, niter)
331
319
 
332
 
    def _close(self):
333
 
        """Close this object and exit"""
334
 
        # First, flush the buffers:
335
 
        self.flush()
336
 
        # Delete back references
337
 
        del self._v_parent
338
 
        del self._v_file
339
 
        del self.type
340
 
        del self.atom
341
 
        del self.filters
342
 
        self.__dict__.clear()
343
320
 
344
321
    def __str__(self):
345
322
        "A compact representation of this class"
346
 
        return "IndexArray(path=%s)" % \
347
 
               (self._v_parent._g_join(self.name))
 
323
        return "IndexArray(path=%s)" % self._v_pathname
 
324
 
348
325
 
349
326
    def __repr__(self):
350
327
        """A verbose representation of this class"""
351
328
 
352
329
        return """%s
353
 
  type = %r
354
 
  shape = %s
355
 
  itemsize = %s
 
330
  atom = %r
356
331
  nrows = %s
357
332
  nelemslice = %s
358
333
  chunksize = %s
359
 
  byteorder = %r""" % (self, self.type, self.shape, self.itemsize, self.nrows,
360
 
                       self.nelemslice, self.chunksize, self.byteorder)
 
334
  byteorder = %r""" % (self, self.atom, self.nrows, self.nelemslice,
 
335
                       self.chunksize, self.byteorder)