Thread: [Pymoney-general] Currency's distrib() method
Brought to you by:
facundobatista
From: Facundo B. <fac...@gm...> - 2005-07-01 08:52:25
|
We've been thinking yesterday how the Currency's distrib method should work. We figured out three posibilities, but we're not be able to choose a winner. The basic idea is to distribute a Currency amount between different parts, and being sure that you're not losing information (well, money, ;). But we saw three methods of doing it: 1. Returning the different currencies, splitting almost equally:: >>> Currency(1).distrib(3) [Currency("0.33"), Currency("0.33"), Currency("0.34")] >>> Currency(1).distrib(7) [Currency("0.14"), Currency("0.14"), Currency("0.14"), Currency("0.14"), Currency("0.15"), Currency("0.15"), Currency("0.14")] The order of the results is not assured. The difference between max(results) and min(results) will be only one Less significant Digit. 2. Returning the almost exact part, with the rest (a kind of divmod() but not to integers):: >>> Currency(1).distrib(3) (Currency("0.33"), Currency("0.01")) >>> Currency(1).distrib(7) (Currency("0.14"), Currency("0.02")) If with the original value OV splited in N parts, it returns the division and the rest (DV, RS), the following applies: DV * N + RS =3D OV 3. Returning the different currencies, splitting differently:: >>> Currency(1).distrib([1,2]) [Currency("0.33"), Currency("0.67")] >>> Currency(3).distrib([2,1,3,3]) [Currency("0.66"), Currency("0.33"), Currency("1.01"), Currency("1.00")= ] The rest of the better-possible-division will be assigned to the greatest(s) value(s) of the result. What do you think? . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/ |
From: Darek S. <ds...@ti...> - 2005-07-01 09:42:39
|
Facundo Batista wrote: > 1. Returning the different currencies, splitting almost equally:: > > >>> Currency(1).distrib(3) > [Currency("0.33"), Currency("0.33"), Currency("0.34")] > >>> Currency(1).distrib(7) > [Currency("0.14"), Currency("0.14"), Currency("0.14"), > Currency("0.14"), Currency("0.15"), Currency("0.15"), > Currency("0.14")] > 2. Returning the almost exact part, with the rest (a kind of divmod() > but not to integers):: > > >>> Currency(1).distrib(3) > (Currency("0.33"), Currency("0.01")) > >>> Currency(1).distrib(7) > (Currency("0.14"), Currency("0.02")) > 3. Returning the different currencies, splitting differently:: > > >>> Currency(1).distrib([1,2]) > [Currency("0.33"), Currency("0.67")] > >>> Currency(3).distrib([2,1,3,3]) > [Currency("0.66"), Currency("0.33"), Currency("1.01"), Currency("1.00")] > > What do you think? Is it possible to have a combination of 1) and 3), with 1) being a special case of 3)? That is, something along the lines of: def distrib(self,nparts=None,proportions=None): if nparts: return self._real_distrib([1] * nparts) return self._real_distrib(proportions) darek |
From: Facundo B. <fac...@gm...> - 2005-07-01 09:51:26
|
On 7/1/05, Darek Suchojad <ds...@ti...> wrote: > Facundo Batista wrote: >=20 > > 1. Returning the different currencies, splitting almost equally:: > ... > > 3. Returning the different currencies, splitting differently:: > ... >=20 > Is it possible to have a combination of 1) and 3), with 1) being > a special case of 3)? That is, something along the lines of: >=20 > def distrib(self,nparts=3DNone,proportions=3DNone): > if nparts: > return self._real_distrib([1] * nparts) > return self._real_distrib(proportions) Yes, it'd be possible. But what basically you're saying is, ok, with the option 3 we include option 1, but with a more complex API. Is really needed to make the API more complex? Because if not, we'll got the (1) and not for the (3)... Regards, . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/ |
From: Darek S. <ds...@ti...> - 2005-07-01 10:10:33
|
Facundo Batista wrote: > Yes, it'd be possible. > > But what basically you're saying is, ok, with the option 3 we include > option 1, but with a more complex API. Is really needed to make the > API more complex? Because if not, we'll got the (1) and not for the > (3)... Heck, I'd even thought about combining 1) 3) and 2) somewhow but decided to not write about it ;-) (I can imagine a situation when I want to explicitly distribute the RS, but well, don't have a strong use-case actually, just an imagination). If you decide it's going to be too complex then feel free to ignore this suggestion. I'm not more than +0 on combining 1) and 3), it seemed to be a simple addition, that's why I proposed it. darek |
From: Facundo B. <fac...@gm...> - 2005-07-01 10:20:08
|
On 7/1/05, Darek Suchojad <ds...@ti...> wrote: > If you decide it's going to be too complex then feel free to ignore > this suggestion. I'm not more than +0 on combining 1) and 3), it seemed > to be a simple addition, that's why I proposed it. I'm not worried about code complex, just how much we increase the complexity of the API to the final user. Personally, I think that is not that much. I'm +0 to expose an API that allows the user to do (1) and (3), pretty much the one you sent before. . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/ |
From: Darek S. <ds...@ti...> - 2005-07-01 11:51:42
|
Facundo Batista wrote: > I'm not worried about code complex, just how much we increase the > complexity of the API to the final user. > > Personally, I think that is not that much. I, too, guess it won't be that complex. > I'm +0 to expose an API that allows the user to do (1) and (3), pretty > much the one you sent before. Then let's add it. We can always get rid of it if it doesn't survive the public discussion on the PEP. darek |
From: Facundo B. <fac...@gm...> - 2005-07-01 14:51:26
|
On 7/1/05, Darek Suchojad <ds...@ti...> wrote: > Then let's add it. We can always get rid of it if it doesn't survive > the public discussion on the PEP. OK. We was forgetting another issue here. Some people don't want the distribution to be to the cent, dime, or whatever, but to a specific value. So, here is what I think will be pretty much the final version of distrib(). Take a look to the examples, because my idea is to include them as test cases. Please send other examples if you feel that is good to test other corners of the problem. ---------------------- distrib(nparts=3DNone, proportions=3DNone, LSV=3DNone) You can call distrib() with nparts XOR proportions. You can not callit with both of them, neither with none of them. In any case, you also can pass LSV. If you call it with ``nparts`` (an integer or something that can be int()'ed), the system will distribute the amount in that quantity of parts, in the same proportion:: >>> Currency(1).distrib(nparts=3D3) (Currency('0.33'), Currency('0.33'), Currency('0.34')) >>> >>> Currency(1).distrib(nparts=3D7) (Currency('0.15'), Currency('0.14'), Currency('0.15'), Currency('0.14'), Currency('0.14'), Currency('0.14'), Currency('0.14')) If you call it with ``proportions`` (a tuple or something that can be tuple()'d; and in each position of the tuple, a Decimal or something that could be Decimal()'ed), the system will distribute the amount regarding those proportions:: >>> Currency(1).distrib(proportions=3D[1,2]) [Currency("0.33"), Currency("0.67")] >>> >>> Currency(3).distrib(proportions=3D[2,1,3,3]) [Currency("0.66"), Currency("0.33"), Currency("1.01"), Currency("1.00")] ``LSV`` means "least significant value, and is the value to which the system will aproximate the finals results. The default is ``10**(-dec_places)``, so if you have ``dec_places=3D2``, ``LSV`` will be ``0.01`` (which is what we used in the examples above). It must be a Decimal or something that can be Decimal()'ed. >>> Currency(1).distrib(nparts=3D3, LSV=3DDecimal("0.1")) (Currency('0.40'), Currency('0.30'), Currency('0.30')) >>> >>> Currency(1).distrib(nparts=3D7, LSV=3D"0.05") (Currency('0.15'), Currency('0.10'), Currency('0.15'), Currency('0.15'), Currency('0.15'), Currency('0.15'), Currency('0.15')) >>> >>> Currency(1).distrib(proportions=3D[1,2], LSV=3D"0.2") [Currency("0.40"), Currency("0.60")] >>> >>> Currency(3).distrib(proportions=3D[2,1,3,3], LSV=3DDecimal("0.15")) [Currency("0.60"), Currency("0.30"), Currency("1.05"), Currency("1.05")] This is needed (at least) in Sweden, where they're used to round to 50 cents when dealing with coins. ---------------- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/ |
From: Jeff K. <jt...@ya...> - 2005-07-02 12:40:31
|
Facundo Batista wrote: > The basic idea is to distribute a Currency amount between different > parts, and being sure that you're not losing information (well, money, > ;). But we saw three methods of doing it: > >>> Currency(1).distrib(3) > [Currency("0.33"), Currency("0.33"), Currency("0.34")] This is an easily understood example of why the distrib() method will be necessary for Currency objects. It hadn't really sunk in until Raymond mentioned pymoney's missing '/' operator on python-dev. Something like the 1-into-3 example should always be prominently mentioned in the documentation, as well as the docstring for distrib(). It should probably also appear in some sort of exception message when a user tries to use division with a Currency object. It would also be useful to show for comparison a correct and efficient distrib() for Decimal objects. If the recommendation is to convert to Decimal when doing averages and other risky/lossy math, then it would be good to show the cannonical Decimal distrib() function in convenient location in the pymoney docs. A distrib() function should not be added to stdlib decimal, obviously. |
From: Jeff K. <jt...@ya...> - 2005-07-02 12:56:12
|
Facundo Batista wrote: > The order of distrib() results is not assured. If it doesn't slow down the distrib() implementation (i.e. only a sort() before returning), then perhaps the order of the results (if splitting on only one divider) should be assured, and selected with a keyword argument, defaulting to descending order. If the results of distrib() are unsorted, most applications using the method (again in the case of a single divider) will implement the same boilerplate to sort in ascending or descending order after each call. distrib_multiple() could be a separate method, to support a list of dividers mentioned in option 3. I think support for multiple dividers should be in there somehow, since it will be kind of tricky for most library users to implement a correct one on their own if they have a need. |