def slicingInfo2IndexSlice(self, slicingInfo): """slicingInfo2Range(slicingInfo) --> slice instance note: slicing is inclusive """ unit = self.unit() start, end = slicingInfo.start, slicingInfo.end # if axis has dimensional unit, convert start and end to pure numbers if necessary if isDimensional(unit): if isDimensional(start): start = start/unit if isDimensional(end): end = end/unit # if axis' unit is a number. it is assumed that the start and end are always given with unit # so we need to divide them by the unit if isNumber(unit) and unit not in [1, 1.0]: if isNumber(start): start = start/unit if isNumber(end): end = end/unit # if start and end are special objects, convert to numbers as well bc = self.binCenters() if start == front: start = bc[0] if end == back: end = bc[-1] # at this point, start and end should all be numbers if not isNumber(start) or not isNumber(end): raise RuntimeError, "At this point, start and end should all be numbers: start=%s(%s), end=%s(%s)" % (start, type(start), end, type(end)) #slice. +1 is due to the difference of bin boundaries and bin centers s = ( self.cellIndexFromValue( start ), self.cellIndexFromValue( end ) + 1 ) return s
def __getitem__(self, s): """Slicing h[ (3.0, 4.0), () ] h[ 3.0, () ] h[ (), () ] h[ (None, 4.0), (999., None ) ] """ if self.errors() is None: raise NotImplementedError , "__getitem__: errors is None" # if s is iterable, we should assume that it is trying to do slicing. # This also means no axis can use iterables as values. if self.dimension() == 1 and '__iter__' not in dir(s): s = (s,) # We also allow use of dictionary. This is a convenient and flexible way # to get or set a slice. if isinstance(s, dict): s = _slicingInfosFromDictionary(s, self.axes()) else: s = _makeSlicingInfos( s, self.dimension() ) #at this point, s is a tuple of SlicingInfo instances. # check sanity of inputs if not isinstance(s, tuple) or len(s) != self.dimension(): raise NotImplementedError , "Histogram[ %s ]. my dimension: %s" % ( s, self.dimension()) # a more meaningful name slicingInfos = s # slicingInfo tuple --> a tuple of index slices indexSlices = self.slicingInfos2IndexSlices( slicingInfos ) #the following line will fail if a dataset is None #should define a special NoneDataset to solve this problem newdatasets = [dataset[indexSlices] for dataset in self.datasets()] #if requested for item instead of slice, return the item now. if isNumber(newdatasets[0]) or isDimensional(newdatasets[0]): return newdatasets #meta data need to be passed to the new histogram newAttrs = self._attributes.copy() #axes of new histogram newAxes = [] for slicingInfo, name in zip(slicingInfos, self.axisNameList()): axis = self.axisFromName( name ) if not isSlicingInfo( slicingInfo ): # if it is not a slicingInfo instance, # it must be a value indexable in this axis. # This is already tested in method "slicingInfo2IndexSlices" value = '%s' % slicingInfo name = axis.name() newAttrs[name] = value else: newAxes.append( axis[ slicingInfo ] ) pass continue #name of new histogram newName = "%s in %s" % ( self.name(), ["%s(%s)"%(axisName, slicingInfo) for axisName, slicingInfo \ in zip(self.axisNameList(), slicingInfos)]) #new histogram new = Histogram( name = newName, unit = self.unit(), data = newdatasets[0], errors = newdatasets[1], axes = newAxes, attributes = newAttrs, slice = True) #addtional datasets. This is not tested yet!!! #probably we should really limit histogram to have only two datasets!!! for i in range(2, len(newdatasets)): ds = newdatasets[i] new.addDataset( ds.name(), ds ) continue return new