I am using Python to learn linear algebra and I have two dictionaries:
v = {1: 1, 2: 8, 3: 0}
and
M = {(1, 2): 2, (3, 1): 1, (3, 3): 7, (2, 1): -1}
and I want to make a dictionary that adds all elements where the keys in v is the same as the first part of the tuples in M. Example of what I want as a answer for the two dictionaries here. (I will show the calculations I want to do)
newDict = {1: 1*M[(1, 2)], 2: 8*M[(2, 1)], 3: 0*M[(3, 1)]+0*M[(3, 3)]
which is the same as:
newDict = {1: 1*2, 2: 8*-1, 3: 0*1+3*7}
so I get a final dictionary in the form
newDict = {1:2, 2:-8, 3:0}
as you can see, I want the same keys as in the dictionary v. The closest I have gotten is this:
>>> [v[k]*M[r] for k in v for r in M if k == r[0]]
[2, -8]
Where I at least have the right answers, but I cannot get this to work. I don't know where to go from here or if I am at the right track at all. Sorry if my explanation might be lacking
Because you are basing values on multiple input keys, use a loop, not a comprehension. Using a collections.defaultdict object makes the logic a little simpler too:
from collections import defaultdict
newDict = defaultdict(int)
for x, y in M:
newDict[x] += M[x, y] * v.get(x, 0)
Output:
>>> from collections import defaultdict
>>> v = {1: 1, 2: 8, 3: 0}
>>> M = {(1, 2): 2, (3, 1): 1, (3, 3): 7, (2, 1): -1}
>>> newDict = defaultdict(int)
>>> for x, y in M:
... newDict[x] += M[x, y] * v.get(x, 0)
...
>>> newDict
defaultdict(<type 'int'>, {1: 2, 2: -8, 3: 0})
How about this..
newD = {k : 0 for k in v}
for k in v:
for r in M:
if k == r[0]: newD[k] += v[k]*M[r]
Related
I'm trying to add two polynomial equations which are in list structure such as below:
[(2,3),(6,2),(-2, 1)], [(-4,3),(2,2),(2,1)]
Which means:
(2x3 + 6x2 - 2x) + (-4x3 + 2x2 + 2x)
I came up with below script to first convert them to dictionary and add the keys.
def combine(p1, p2):
total = {}
d1 = dict(p1)
d2 = dict(p2)
print('The dictionaries are: ', d1, d2)
for e in d1.keys():
for p in d2.keys():
if d1[e] == d2[p]:#add only if the exponential match
total[e + p] = d2[p]
print(total)
This works fine for some value and provide incorrect results for some other values. I'm not entirely sure what is wrong with the above script.
Example 1: (working fine)
>>> combine([(4,3),(3,2),(10, 1)], [(-4,3),(2,2),(8,1)])
The dictionaries are: {4: 3, 3: 2, 10: 1} {-4: 3, 2: 2, 8: 1}
{0: 3}
{0: 3, 5: 2}
{0: 3, 5: 2, 18: 1}
Example 2: (Incorrect Result)
>>> combine([(4,3),(3,2),(10, 1)], [(-4,3),(2,2),(-10,1)])
The dictionaries are: {4: 3, 3: 2, 10: 1} {-4: 3, 2: 2, -10: 1}
{0: 3}
{0: 3, 5: 2}
{0: 1, 5: 2}
Example 3: (Incorrect Result)
>>> combine([(2,3),(6,2),(-2, 1)], [(-4,3),(2,2),(2,1)])
The dictionaries are: {2: 3, 6: 2, -2: 1} {-4: 3, 2: 1}
{-2: 3}
{-2: 3, 0: 1}
Can you please let me know to identify the issues in the above script?
Using one dictionary with the exponents as keys and coefficients as values:
from collections import defaultdict
def combine(p1, p2):
total = defaultdict(int)
for coefficient, exponent in p1 + p2:
total[exponent] += coefficient
return [item[::-1] for item in total.items()]
p1, p2 = [(2,3),(6,2),(-2, 1)], [(-4,3),(2,2),(2,1)]
print(combine(p1, p2))
Output:
[(-2, 3), (8, 2), (0, 1)]
If you represented the terms not as (coefficient, exponent) but as (exponent, coefficient) then you could just end with this:
return list(total.items())
Then again, list comprehension makes it easy to filter zeros if you want (you didn't say):
return [(coefficient, exponent)
for exponent, coefficient in total.items()
if coefficient]
The problem is that when multiple terms have the same coefficient after being added, they will have the same key. This means that the values will be overwritten as the iteration continues.
My solution would to actually make the exponent as the key and the coefficient as the value since the exponents will be unique:
def switch(p1):
newP = []
for index, tuple in enumerate(p1):
newP.append((tuple[1],tuple[0]))
return newP
This function takes in a list of tuples as its parameter, and switches the order. Thus, when we convert them to dictionaries, the keys and values will switch, allowing the keys to be exponents:
def combine(p1, p2):
total = {}
p1 = switch(p1)
p2 = switch(p2)
d1 = dict(p1)
d2 = dict(p2)
print('The dictionaries are: ', d1, d2)
for e in d1.keys():
for p in d2.keys():
if e == p:#add only if the exponential match
total[e] = d1[e] + d2[p]
print(printPoly(total))
I created a function, printPoly() to make the printing more readable:
def printPoly(total):
output = ""
for key, value in total.items():
output += str(value) + "x^" + str(key) + "+"
return output[0:-1]
Now lets test our final code:
def switch(p1):
newP = []
for index, tuple in enumerate(p1):
newP.append((tuple[1],tuple[0]))
return newP
def combine(p1, p2):
total = {}
p1 = switch(p1)
p2 = switch(p2)
d1 = dict(p1)
d2 = dict(p2)
print('The dictionaries are: ', d1, d2)
for e in d1.keys():
for p in d2.keys():
if e == p:#add only if the exponential match
total[e] = d1[e] + d2[p]
print(printPoly(total))
def printPoly(total):
output = ""
for key, value in total.items():
output += str(value) + "x^" + str(key) + "+"
return output[0:-1]
Test: combine([(4,3),(3,2),(10, 1)], [(-4,3),(2,2),(8,1)])
Output: 0x^3+5x^2+18x^1
Test: combine([(4,3),(3,2),(10, 1)], [(-4,3),(2,2),(-10,1)])
Output: 0x^3+5x^2+0x^1
Test: combine([(2,3),(6,2),(-2, 1)], [(-4,3),(2,2),(2,1)])
Output: -2x^3+8x^2+0x^1
NOTE: If you do not want to use the printPoly() function to get it in a polynomial form, you can simply change:
print(printPoly(total))
to:
print(total)
though simply printing total will list exponents first, and then the coefficient.
I hope this helped! Please let me know if you need any further clarification or details :)
(I really enjoyed this problem)
Let's define data.
data = [[(2,3),(6,2),(-2, 1)], [(-4,3),(2,2),(2,1)]]
Now, let's write a generator expression to flatten that.
(y for x in data for y in x)
And import itertools.groupby.
from itertools import groupby
Next we'll define a couple of utility functions to extract element from two element tuples.
def fst(tpl): return tpl[0]
def snd(tpl): return tpl[1]
Now we can sort the flattened data by the exponent value, in descending order.
sorted((y for x in data for y in x), key=snd, reverse=True)
This gives us:
[(2, 3), (-4, 3), (6, 2), (2, 2), (-2, 1), (2, 1)]
And using groupby:
groupby(sorted((y for x in data for y in x), key=snd, reverse=True), key=snd)
Exponents are the keys here, and the coefficient/exponent tuples matching them are the values.
Now we just need a list comprehension in which we'll use map to extract all of the coefficients and sum to add them.
[(sum(map(fst, c)), e) for e, c in groupby(sorted((y for x in data for y in x), key=snd, reverse=True), key=snd)]
End result?
[(-2, 3), (8, 2), (0, 1)]
I tried to add the string elements to the dictionary but it doesnt work
adatok = ["marha", 1, "kacsa", 2, "kacsa", 1, "birka", 3, "marha", 4, "marha", 2]
sorszámok = list(set([x for x in adatok if type(x) == int]))
szótár = {}
for i in sorszámok:#Létrehozzuk a sorszámokat egy szótárba
szótár.update({i:set()})
for j in adatok:
if type(j) == int:
szótár[j].add(adatok[adatok.index(j)-1])
#Expected output:{1: {'marha','kacsa'}, 2: {'kacsa','marha'}, 3: {'birka'}, 4: {'marha'}}
#Output:{1: {'marha'}, 2: {'kacsa'}, 3: {'birka'}, 4: {'marha'}}
Instead of starting with unique keys and then trying to find the values for each key, iterate over the key, value pairs and add them to the sets. Using defaultdict saves you the trouble of setting up the dict ahead of time.
>>> from collections import defaultdict
>>> adatok = ["marha", 1, "kacsa", 2, "kacsa", 1, "birka", 3, "marha", 4, "marha", 2]
>>> szótár = defaultdict(set)
>>> for k, v in zip(adatok[1::2], adatok[0::2]):
... szótár[k].add(v)
...
>>> dict(szótár)
{1: {'marha', 'kacsa'}, 2: {'marha', 'kacsa'}, 3: {'birka'}, 4: {'marha'}}
I have a dictionary called wordCounts which maps a word to how many times it occurred, how can I get the top n words in the dict while allowing more than n if there is a tie?
As the previous answer says, you can cast as a Counter to make this dataset easier to deal with.
>>> from collections import Counter
>>> d = {"d":1,"c":2,"a":3,'b':3,'e':0,'f':1}
>>> c = Counter(d)
>>> c
Counter({'b': 3, 'a': 3, 'c': 2, 'f': 1, 'd': 1, 'e': 0})
Counter has a most_common(n) method that will take the n most common elements. Note that it will exclude ties. Therefore:
>>> c.most_common(4)
[('b', 3), ('a', 3), ('c', 2), ('f', 1)]
To include all values equal to the nth element, you can do something like the following, without converting to a Counter. This is pretty messy, but it should do the trick.
from collections import Counter
def most_common_inclusive(freq_dict, n):
# find the nth most common value
nth_most_common = sorted(c.values(), reverse=True)[n-1]
return { k: v for k, v in c.items() if v >= nth_most_common }
You can use as follows:
>>> d = {'b': 3, 'a': 3, 'c': 2, 'f': 1, 'd': 1, 'e': 0}
>>> most_common_inclusive(d, 4)
{'d': 1, 'b': 3, 'c': 2, 'f': 1, 'a': 3}
One solution could be:
from collections import Counter, defaultdict
list_of_words = ['dog', 'cat', 'moo', 'dog', 'pun', 'pun']
def get_n_most_common(n, list_of_words):
ct = Counter(list_of_words)
d = defaultdict(list)
for word, quantity in ct.items():
d[quantity].append(word)
most_common = sorted(d.keys(), reverse= True)
return [(word, val) for val in most_common[:n] for word in d[val]]
And the tests:
>> get_n_most_common(2, list_of_words)
=> [('pun', 2), ('dog', 2), ('moo', 1), ('cat', 1)]
>> get_n_most_common(1, list_of_words)
=> [('pun', 2), ('dog', 2)]
MooingRawr is on the right track, but now we need to just get the top n results
l = []
for i, (word, count) in enumerate(sorted(d.items(), reverse=True, key=lambda x: x[1])):
if i >= n and count<l[-1][1]:
break
l.append((word, count))
I try to sort this
{1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
to this
{3: 4, 4: 3, 1: 2, 2: 1, 0: 0}
with a:b model where sort by most higher b value
import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1), reverse=True)
print(sorted_x)
First result is good but in list form:
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
And I tried to
print(dict(sorted_x))
But…
{0: 0, 1: 2, 2: 1, 3: 4, 4: 3}
How I can save correctly sorted result and convert it to the dict format?
Thank you so much
upd:
OrderedDict([(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)])
I cant understand how OrderedDict can solve my problem
>>> from collections import OrderedDict
>>> sorted_dict = OrderedDict()
>>> dct = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> dct.values().sort()
>>> for x in sorted(dct.values(), reverse=True):
... keys = [idx for idx in dct if dct[idx]==x]
... for key in keys:
... sorted_dict[key] = x
...
>>>
>>> sorted_dict
OrderedDict([(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)])
>>>
or if you are dead set on writing something that looks like dict syntax to a file,
>>> def print_dict_looking_thing(ordered_dict):
... print "{"
... for key, value in ordered_dict.iteritems():
... print "{0}: {1},".format(key, value)
... print "}"
...
>>> print_dict_looking_thing(sorted_dict)
{
3: 4,
4: 3,
1: 2,
2: 1,
0: 0,
}
That is how OrderedDict "solves your problem". But, a few points worth reemphasizing:
Dicts are unordered. You can't "sort them", only operate on their keys and values.
OrderedDicts remember the order that items were added.
It sounds like you are trying to print to a file something that looks like dict syntax. If you do that and read the dict into python at some point, it will become an unordered object (a dict) again.
It is not super clear what "your problem" is. What is the desired end state; printing a string that reads like dict syntax? Making something that has the accessor methods that dicts do? Clarifying what exactly you want (in terms of performance) would be helpful.
The code above has O(n**2) complexity. Don't use it on big things.
Dictionary can't be ordered. But you can use class collections.OrderedDict.
this is what I did. is there a better way in python?
for k in a_list:
if kvMap.has_key(k):
kvMap[k]=kvMap[k]+1
else:
kvMap[k]=1
Thanks
Use defaultdict
from collections import defaultdict
kvmap= defaultdict(int)
for k in a_list:
kvmap[k] += 1
Single element:
a_list.count(k)
All elements:
counts = dict((k, a_list.count(k)) for k in set(a_list))
I dunno, it basically looks fine to me. Your code is simple and easy to read which is an important part of what I consider pythonic.
You could trim it up a bit like so:
for k in a_list:
kvMap[k] = 1 + kvMap.get(k,0)
Such an old question, but considering that adding to a defaultdict(int) is such a common use, It should come as no surprise that collections has a special name for that (since Python 2.7)
>>> from collections import Counter
>>> Counter([1, 2, 1, 1, 3, 2, 3, 4])
Counter({1: 3, 2: 2, 3: 2, 4: 1})
>>> Counter("banana")
Counter({'a': 3, 'n': 2, 'b': 1})
Another solution exploits setdefault():
for k in a_list:
kvMap[k] = kvMap.setdefault(k, 0) + 1
If your list is sorted, an alternative way would be to use itertools.groupby. This might not be the most effective way, but it's interesting nonetheless. It retuns a dict of item > count :
>>> import itertools
>>> l = [1,1,2,3,4,4,4,5,5,6,6,6,7]
>>> dict([(key, len([e for e in group]))
for (key, group)
in itertools.groupby(l)])
{1: 2, 2: 1, 3: 1, 4: 3, 5: 2, 6: 3, 7: 1}