An equivalent ofnumpy.linspace, but as a pure-Python lazy sequence.
Like NumPy'slinspace, but unlike thespread andfrange recipes listed here, thenum argument specifies the number of values, not the number of intervals, and the range is closed, not half-open.
Although this is primarily designed for floats, it will work forFraction,Decimal, NumPy arrays (although this would be silly) and evendatetime values.
This recipe can also serve as an example for creating lazy sequences.
See the discussion below for caveats.
1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637 | classlinspace(collections.abc.Sequence):"""linspace(start, stop, num) -> linspace object Return a virtual sequence of num numbers from start to stop (inclusive). If you need a half-open range, use linspace(start, stop, num+1)[:-1]. """def__init__(self,start,stop,num):ifnotisinstance(num,numbers.Integral)ornum<=1:raiseValueError('num must be an integer > 1')self.start,self.stop,self.num=start,stop,numself.step=(stop-start)/(num-1)def__len__(self):returnself.numdef__getitem__(self,i):ifisinstance(i,slice):return[self[x]forxinrange(*i.indices(len(self)))]ifi<0:i=self.num+iifi>=self.num:raiseIndexError('linspace object index out of range')ifi==self.num-1:returnself.stopreturnself.start+i*self.stepdef__repr__(self):return'{}({}, {}, {})'.format(type(self).__name__,self.start,self.stop,self.num)def__eq__(self,other):ifnotisinstance(other,linspace):returnFalsereturn((self.start,self.stop,self.num)==(other.start,other.stop,other.num))def__ne__(self,other):returnnotself==otherdef__hash__(self):returnhash((type(self),self.start,self.stop,self.num)) |
For Python 3.3+ code, usecollections.abc.Sequence instead ofcollections.Sequence.
There are two obvious simple algorithms forlinspace (plus some more advanced ones):
start + i*(stop-start)/(num-1)(stop*i + start*(num-i-1))/(num-1)This recipe uses the former, primarily because it's the oneused by NumPy.
Both are simple and fast; neither accumulates errors (both will close to the minimum possible number of 1 ulp errors distributed evenly throughout the range, which is as good as you can hope for with floats); but neither is perfect:
0. (SeeNumPy bug #5437)inf.linspace(0, 1, 11)[3] == 0.30000000000000004).datetime. (Note that division first only multiplies and divides _differences_ between values—so, withdatetime,timedeltas.)For many lazy sequences, a slice should return an instance of the same sequence. This is how the builtinrange works, for instance. However, floating point rounding makes that impossible forlinspace; a slice could at best guarantee a sequence whose values are within 2 ulp of the original values. So, a slice instead returns a list.
Inheriting fromSequence means thatlinspace provides__contains__,index, andcount methods, using the default (linear-search) implementation. It's generally a bad idea to use these (for the same reason it's a bad idea to compare floats with==), but not providing them would meanlinspace is no longer aSequence. Of course anO(1) implementation could be provided pretty easily, but that would just encourage (mis)use.
| Created byAndrew BarnertonMon, 12 Jan 2015(MIT) |
| ◄ | Python recipes (4591) | ► |
| ◄ | Andrew Barnert's recipes (2) | ► |
Privacy Policy |Contact Us |Support
© 2024 ActiveState Software Inc. All rights reserved. ActiveState®, Komodo®, ActiveState Perl Dev Kit®, ActiveState Tcl Dev Kit®, ActivePerl®, ActivePython®, and ActiveTcl® are registered trademarks of ActiveState. All other marks are property of their respective owners.