orange
orange

Reputation: 101

Calculate classes' percentage with dict?

I'm kind of stuck here, where it seems that I am missing something. I need to calculate percentages for each class of a legend over a dictionnairy of listed elements. Here is my content:

Legend = {u'Conifer': 1.0, u'Hardwood': 4.0, u'Field': 5.0, u'Urban': 6.0, u'Water': 8.0}
PolyItems = {u'5423': [1.0, 2.0, 1.0, 4.0],
             u'425': [6.0, 6.0, 6.0, 6.0, 8.0, 1.0, 6.0, 1.0, 4.0, 4.0, 4.0],
             u'9756': [1.0, 1.0, 4.0],
             u'6418': [4.0, 8.0, 1.0, 1.0]}

Now, I was trying to make a loop over the Legend variable and use a lambda function to calculate the percentages of each class, but I always get 0.0 for each items.

nb = lambda v, c: float(len([x for x in v if v==c]))
nbtot = lambda v: float(len(v))
for name, cla in Legend.iteritems():
    result = {id: nb(vals, cla)/nbtot(vals) * 100
                for id, vals in PolyItems.items() if len(id) > 0}

Can I use a lambda function for this? Is there a better way to achieve this?

Upvotes: 0

Views: 169

Answers (2)

martineau
martineau

Reputation: 123473

I think the nb lambda should be defined as shown:

from operator import itemgetter

Legend = {u'Conifer': 1.0, u'Hardwood': 4.0, u'Field': 5.0, u'Urban': 6.0, u'Water': 8.0}
PolyItems = {u'5423': [1.0, 2.0, 1.0, 4.0],
             u'425' : [6.0, 6.0, 6.0, 6.0, 8.0, 1.0, 6.0, 1.0, 4.0, 4.0, 4.0],
             u'9756': [1.0, 1.0, 4.0],
             u'6418': [4.0, 8.0, 1.0, 1.0]}

nb = lambda v, c: v.count(c)
nbtot = lambda v: float(len(v))
longest_name = max(len(name) for name in Legend)

for name, cla in sorted(Legend.iteritems(), key=itemgetter(1)):
    result = {id: nb(vals, cla)/nbtot(vals) * 100
                for id, vals in PolyItems.items() if len(id) > 0}
    print('{:<{width}}(class {}): {}'.format(name, cla, result, width=longest_name+1))

Output:

Conifer  (class 1.0): {u'9756': 66.66666666666666, u'425': 18.181818181818183, u'6418': 50.0, u'5423': 50.0}
Hardwood (class 4.0): {u'9756': 33.33333333333333, u'425': 27.27272727272727, u'6418': 25.0, u'5423': 25.0}
Field    (class 5.0): {u'9756': 0.0, u'425': 0.0, u'6418': 0.0, u'5423': 0.0}
Urban    (class 6.0): {u'9756': 0.0, u'425': 45.45454545454545, u'6418': 0.0, u'5423': 0.0}
Water    (class 8.0): {u'9756': 0.0, u'425': 9.090909090909092, u'6418': 25.0, u'5423': 0.0}

Note: The two lambda functions aren't really necessary since Python has built-ins that perform those operations. This means the code can be simplified somewhat by omitting them—i.e. the following code will produce exactly the same output:

longest_name = max(len(name) for name in Legend)

for name, cla in sorted(Legend.iteritems(), key=itemgetter(1)):
    result = {id: vals.count(cla)/float(len(vals)) * 100
                for id, vals in PolyItems.items() if len(id) > 0}
    print('{:<{width}}(class {}): {}'.format(name, cla, result, width=longest_name+1))

Upvotes: 1

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52163

nb = ... if v==c]))

You are comparing a list v against a float value c in nb which always returns False making the function produce 0.0 for any parameters.

I don't know what that function is for but I think it should be defined as:

nb = lambda v, c: float(len([x for x in v if x==c]))

Upvotes: 1

Related Questions