Searching for most pythonic/least stupid way to do something simple

D

david jensen

Hi all,

This may be a complete brainfart, but it's been puzzling me for a day
or two (!).
Sorry for not describing "something" in the subject, but it's hard to
describe succinctly:

I have a short list of non-zero positive integers (say
myList=[2,5,8,3,5]). I need to return five lists of non-negative
numbers, such that for five different "share sizes", myList[0] and
myList[1] will share twice the smaller amount...

def getOutcomes():
outcomes=[]
if myList[0]<=myList[1]:
amountToShare=2*myList[0]
remainder=myList[1]-myList[0]
outcome.append((amountToShare, remainder)+myList[2:]) #
shares are (100%, 0)
outcome.append((amountToShare*0.75, remainder
+amountToShare*0.25)+myList[2:]) #shares are (75%, 25%), and exactly
the same for (50%,50%), (25%, 75%), and (0,100%)
....
....
return outcomes

i.e. for the above myList, outcomes=[[4,1,8,3,5], [3,2,8,3,5],
[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

if myList[0]>myList[1], i want exactly the opposite to happen (i.e.,
just switching what happens to positions 0 and 1)

Obviously, i can just write the code again, in an else, switching
indices 0 and 1. Or, I could just have a test at the beginning, switch
them if they are in the order "big, small", and then switch them again
at the end in a list comprehension. Both ideas seem terribly silly,
and there must be an obvious way to do it that I'm not seeing.

any help?

many thanks

dmj
 
D

david jensen

.... and of course i screwed up my outcomes... that should read
outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]
 
N

nn

david said:
... and of course i screwed up my outcomes... that should read
outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

For starters:

def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=myList[2:]
outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]
+ tail
for perc in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes
 
G

Gerard Flanagan

david said:
... and of course i screwed up my outcomes... that should read
outcomes=[[4,3,8,3,5],[3,4,8,3,5],[2,5,8,3,5],[1,6,8,3,5],[0,7,8,3,5]]

abstracting the given algorithm:

def iterweights(N):
d = 1.0/(N-1)
for i in xrange(N):
yield i*d, (N-1-i)*d

def iterparts(x0, x1, N):
a = min(x0, x1)
b = max(x0, x1)
s = 2 * a
t = b - a
for m, n in iterweights(N):
if a == x0:
yield s*m, s*n+t
else:
yield s*n+t, s*m

for p in iterparts(2, 5, 5):
print p

print

for p in iterparts(5, 2, 5):
print p

(0.0, 7.0)
(1.0, 6.0)
(2.0, 5.0)
(3.0, 4.0)
(4.0, 3.0)

(7.0, 0.0)
(6.0, 1.0)
(5.0, 2.0)
(4.0, 3.0)
(3.0, 4.0)
 
P

Paul Rubin

david jensen said:
Obviously, i can just write the code again, in an else, switching
indices 0 and 1. Or, I could just have a test at the beginning, switch
them if they are in the order "big, small", and then switch them again
at the end in a list comprehension. Both ideas seem terribly silly,
and there must be an obvious way to do it that I'm not seeing.

Generally when faced with this kind of question, see if you can
use the built-in min and max functions:

def getOutcomes():
outcomes=[]
smaller = min(myList[0], myList[1])
bigger = max(myList[0], myList[1])
amountToShare=2*smaller
remainder = bigger - smaller
...
 
D

david jensen

Thank you both very much!

Yeah: it was a total brainfart on my part: nn's solution should have
been obvious.

As the general solution, i like your approach, Gerard, but I think
I'll stick to nn's, the one i should have written.

Again, thank you both!

dmj
 
D

david jensen

Thanks Paul, but i don't immediately see how that helps (maybe I'm
just being dense)... nn's solution, though i initially thought it
worked, actually has a similar problem:

intended:
[[6, 1, 5, 5], [4.5, 2.5, 5, 5], [3, 4, 5, 5], [1.5, 5.5, 5, 5], [0,
7, 5, 5]]
[[7, 0, 5, 5], [5.5, 1.5, 5, 5], [4, 3, 5, 5], [2.5, 4.5, 5, 5], [1,
6, 5, 5]]

nn's solution:
[[6.0, 1.0, 5, 5], [4.5, 2.5, 5, 5], [3.0, 4.0, 5, 5], [1.5, 5.5, 5,
5], [0.0, 7.0, 5, 5]]


it's obvious why this is happening (the list comprehension assumes
that index 0 is, after all, index 0), but not immediately obvious how
to fix it, except by going with gerard's solution or doing what i did
in the beginning: flip them if they're in the wrong order, remember,
and flip them again at the end.

dmj
 
D

david jensen

of course, changing nn's to:
def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=list(myList[2:])
outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]+
tail for perc in (1.0, 0.75, 0.5, 0.25, 0.0)] if not low_id else
[[remainder+amountToShare*perc, amountToShare*(1-perc)]+ tail for perc
in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes


works, just hides the ugliness in a more compact form
 
M

Michael Torrie

david said:
of course, changing nn's to:
def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=list(myList[2:])
outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]+
tail for perc in (1.0, 0.75, 0.5, 0.25, 0.0)] if not low_id else
[[remainder+amountToShare*perc, amountToShare*(1-perc)]+ tail for perc
in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes


works, just hides the ugliness in a more compact form

If Gerard's code works, I would consider it far superior to your code
here. Pythonic does not necessarily mean short and ugly, nor does it
mean that you have to always use list comprehensions. Having a
readable algorithm that's easy to follow in the future is a far better
way than trying to use python's cool features to compact the code to as
small and unreadable section as possible.

I used to use list comprehension all the time, but I've found that often
an explicit for loop is a much better solution in terms of
maintainability. Especially when you start seeing nested comprehensions
such as you have here.
 
D

david jensen

If Gerard's code works, I would consider it far superior to your code
here. Pythonic does not necessarily mean short and ugly

yes, I agree... and in my script i'm using something very like
Gerard's (thanks again, Gerard). I just posted the corrected version
of nn's because the original solved only half the problem.

thanks, all

dmj
 
N

nn

Michael said:
david said:
of course, changing nn's to:
def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
amountToShare = 2*myList[low_id]
remainder = myList[not low_id]-myList[low_id]
tail=list(myList[2:])
outcomes = [[amountToShare*perc, remainder+amountToShare*(1-perc)]+
tail for perc in (1.0, 0.75, 0.5, 0.25, 0.0)] if not low_id else
[[remainder+amountToShare*perc, amountToShare*(1-perc)]+ tail for perc
in (1.0, 0.75, 0.5, 0.25, 0.0)]
return outcomes


works, just hides the ugliness in a more compact form

If Gerard's code works, I would consider it far superior to your code
here. Pythonic does not necessarily mean short and ugly, nor does it
mean that you have to always use list comprehensions. Having a
readable algorithm that's easy to follow in the future is a far better
way than trying to use python's cool features to compact the code to as
small and unreadable section as possible.

I used to use list comprehension all the time, but I've found that often
an explicit for loop is a much better solution in terms of
maintainability. Especially when you start seeing nested comprehensions
such as you have here.

To be fair, that list comprehension was unnecessary complicated. The
following version does the same thing and still looks pretty
reasonable IMHO:

def getOutcomes(myList=[2,5,8,3,5]):
low_id = int(myList[0]>myList[1])
high_id = not low_id
smaller = myList[low_id]
bigger = myList[high_id]
amountToShare = 2*smaller
remainder = bigger-smaller
remain0 = low_id*remainder
remain1 = high_id*remainder
tail = list(myList[2:])
percents = (1.0, 0.75, 0.5, 0.25, 0.0)
outcomes = [[remain0+amountToShare*perc, remain1+amountToShare*(1-
perc)]
+tail for perc in percents]
return outcomes
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top