I'm making a gravity simulator and I need to calculate the resultant force acting upon each body.
In order to do this, I need to iterate through every pair of bodies in a dictionary (id: instance of Body class) and get the gravitational force between those two bodies. Then, I would add up all the forces and get the resultants.
But, how do I iterate over each pair of items in a dictionary only once in Python? If the celestial bodies were kept in a list, it would be simple:
for i in range(len(bodies)):
for j in range(len(bodies) - i - 1):
k = j - i + 1
b1 = bodies[i]
b2 = bodies[k]
values() and itertools' combinations are ideal for this use case.
from itertools import combinations
for a, b in combinations(bodies.values(), 2):
print a, b
you're looking for itertools.combinations():
An example:
In [76]: lis=['a','b','c','d'] #consider these as your dictionary items
In [77]: [x for x in combinations(lis,2)]
Out[77]: [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
The itertools module provides an excellent combinations method you could use:
from itertools import combinations
bodies = {}
# add bodies
for a,b in combinations(bodies.values(), 2):
# a and b are a pair of bodies. do stuff
pass
Incidentally, this will still work even if you use a list:
from itertools import combinations
bodies = []
# add bodies
for a,b in combinations(bodies, 2):
pass
Related
This question already has answers here:
Finding the average of a list
(25 answers)
Closed 1 year ago.
I have the following list of tuples:
list = [(120, 'x'), (1120, 'y'), (1330, 'x'), (0, 't'), (1, 'x'), (0, 'd'), (2435, 'x')]
I would like to calculate the mean of the first component of all tuples. I did the following:
s = []
for i in range(len(list)):
a = list[0][i]
if a =! 0:
s.append(a)
else:
pass
mean = sum(s) / len(s)
and it works, but my question is whether there is any way to avoid using for loops? since I have a very large list of tuples and due to time calculation I need to find another way if that possible.
According to the above stated for loop method. How could I find the mean with regard to the wights? I mean, e.g. the last element in the list is (2435, 'x') and the number 2435 is very large in comparison to that one in (1, 'x') which is 1. Any ideas would be very appreciated. Thanks in advance.
The loop is unavoidable as you need to iterate over all the elements at least once as John describes.
However, you can use an iterator based approach to get rid of creating a list to save on space:
mean = sum(elt[0] for elt in lst)/len(lst)
Update: I realize you only need the mean of elements that are non-zero. You can modify your approach to not store the elements in the list.
total = 0
counts = 0
for elt in lst:
if elt[0]:
total += elt[0]
counts += 1
mean = total/counts
A pandas approach:
import pandas as pd
tuples = [(120, 'x'), (1120, 'y'), (1330, 'x'), (0, 't'), (1, 'x'), (0, 'd'), (2435, 'x')]
df = pd.DataFrame(tuples)
df[0][df[0]!=0].mean() #1001.2
Careful timing would be needed to see if this is any better than what you are currently doing. The actual mean calculation should be faster, but the gain could well be negated by the cost of conversion.
You do need a for loop, but you can use list comprehension to make it cleaner.
Also, python standard library has a very nice statistics module that you can use for the calculation of the mean.
As extra note, please, do not use list as a variable name, it can be confused with the type list.
from statistics import mean
mylist = [(120, 'x'), (1120, 'y'), (1330, 'x'), (0, 't'), (1, 'x'), (0,'d'), (2435, 'x')]
m = mean([item[0] for item in mylist if item[0] != 0])
print(m)
1001.2
In Python 2.7
items = [item[0] for item in mylist if item[0] != 0]
mean = sum(items)/len(items)
print(mean)
1001.2
Finish up by refactoring the list comprehension to show more meaningful variable names, for example items = [number for number, letter in mylist if number != 0]
I have a function to process the list of tuples. What is the easiest way to include for loop inside the function itself? I am fairly new to python, trying to convert this in OOP function. Any help will be appreciated.
My current solution:
tups = [(1,a),(2,b),(5,t)]
def func(a,b):
# do something for a and b
return (c,d)
output = []
for x, y in tups:
output.append(func(x,y))
output will be
[(c,d),(m,n),(h,j)]
I think map is more suitable for your use case
tups = [(1,"a"),(2,"b"),(5,"t")]
def func(z):
# some random operation say interchanging elements
x, y = z
return y, x
tups_new = list(map(func, tups))
print(tups_new)
Output:
[('a', 1), ('b', 2), ('t', 5)]
just write your loop in func:
tups = [(1,a),(2,b),(5,t)]
def func(tuples):
for a, b in tuples:
# do something for a and b
result.append((c,d))
return result
output = []
output.append(func(tups))
Just do this with list comprehensions:
tups = [(1,"a"),(2,"b"),(5,"t")]
print([(obj[1], obj[0]) for obj in tups])
# [('a', 1), ('b', 2), ('t', 5)]
I am aware of dictionaries and collection.Counters in Python.
My question is how can I make one that takes index of the string into account?
For example for this string: aaabaaa
I would like to make a tuples that contain each string in progression, keeping track of the count going left to right and resetting the count once a new alphanumeric is found.
For example, I like to see this output:
[('a', 3), ('b', 1), ('a', 3)]
Any idea how to use the dictionary / Counter/ or is there some other data structure built into Python I can use?
Regards
You could use groupby:
from itertools import groupby
m = [(k, sum(1 for _ in v)) for k, v in groupby('aaabaaa')]
print(m)
Output
[('a', 3), ('b', 1), ('a', 3)]
Explanation
The groupby function makes an iterator that returns consecutive keys and groups from the iterable, in this case 'aaabaaa'. The key k is the value identifying of the group, ['a', 'b', 'a']. The sum(1 for _ in v) count the amount of elements in the group.
Is there a simpler way to iterate over multiple strings than a massive amount of nested for loops?
list = ['rst','uvw','xy']
for x in list[0]:
for y in list[1]:
for z in list[2]:
print x+y+z
rux
ruy
...
tvx
tvy
twx
twy
Example list I really want to avoid typing writing loops for:
list = ['rst','uvw','xy','awfg22','xayx','1bbc1','thij','bob','thisistomuch']
You need itertools.product:
import itertools
list = ['rst','uvw','xy','awfg22','xayx','1bbc1','thij','bob','thisistomuch']
for x in itertools.product(*list):
print(''.join(x))
product returns all possible tuples of elements from iterators it gets. So
itertools.product('ab', 'cd')
will return a generator, yielding ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd')
You are looking for the product function from itertools:
import itertools
lst = ['rst','uvw','xy']
[''.join(s) for s in itertools.product(*lst)]
# ['rux',
# 'ruy',
# 'rvx',
# 'rvy',
# 'rwx',
# ...
# 'twx',
# 'twy']
Another way? Definitely. Simpler? Maybe not...
I'm guessing it's because you don't necessarily know how many strings you'll have in your list.
What about:
sl = ['abc','mno','xyz']
def strCombo(l,s=''):
if(len(l)==0):
return s
elif(len(l)==1):
return [(s+x) for x in l[0]]
else:
return [strCombo(l[1:],(s+x)) for x in l[0]]
final = []
for x in strCombo(sl)[0]:
final = final + x
I need to find the key whose value is the lowest in the ordered dictionary but only when it is True for the position in my_list.
from collections import OrderedDict
my_list = [True,False,False,True,True,False,False,False,]
my_lookup = OrderedDict([('a', 2), ('b', 9), ('c', 4), ('d', 7),
('e', 3), ('f', 0), ('g', -5), ('h', 9)])
I know how to do it with a for loop like
mins=[]
i=0
for item in my_lookup.items():
if my_list[i]:
mins.append(item)
i+=1
print min(mins,key=lambda x:x[1])[0]
prints
a
because a is lowest that is also True in my_list.
This works but it is long and I want to know how to do it with comprehensions or one line?
You can use itertools.compress with key to min being get method,
>>> from itertools import compress
>>> min(compress(my_lookup, my_list), key=my_lookup.get)
a
You can also combine generator expression and min:
>>> min(((value, key) for ((key, value), flag) in zip(my_lookup.iteritems(), my_list) if flag))[1]
'a'
Two-liner:
from itertools import izip
print min((lookup_key for (lookup_key, keep) in izip(my_lookup.iterkeys(),
my_list) if keep), key=my_lookup.get)
For what it's worth, a slight variation of the original code performs reasonably (on par with ovgolovin's approach) and is quite readable:
minimum = (None, float('inf'))
for i, item in enumerate(my_lookup.items()):
if not my_list[i]:
continue
if item[1] < minimum[1]:
minimum = item
return minimum[0]
Even the original code is only slightly slower than this, based on ovgolovin's ideone benchmark. Of course, Jared's solution is quite faster and quite shorter.