0

I am trying to apply callout labels as asked here: Python PieChart (is it possible to do CallOut labels)

However, I'm getting a number of issues trying to apply the overlapping labels.. any ideas... I am a beginner with Python.

Errors such as AttributeError: 'list' object has no attribute 'theta2' TypeError: 'function' object is not iterable AttributeError: 'numpy.int64' object has no attribute 'theta2'

here is my code:

    from collections import Counter
    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    import matplotlib.cm as cm, matplotlib.font_manager as fm
    import pandas as pd
    import csv
    import itertools
    from collections import OrderedDict
    import operator 


# Create database of duplicates - check if the mac and os pairs have duplicates
    reader = csv.reader(open('Workbook1.csv', 'r'), delimiter=',')
    writer = csv.writer(open('remacos.csv', 'w'), delimiter=',')
    entries = set()

    for row in reader:
        key = (row[1], row[2])

    if key not in entries:
        writer.writerow(row)
        entries.add(key)

    del writer #Force the writer to clean up



# Create database of duplicates - check if the mac and browser pairs have duplicates
    reader = csv.reader(open('Workbook1.csv', 'r'), delimiter=',')
    writer = csv.writer(open('remacbrowser.csv', 'w'), delimiter=',')
    entries = set()

    for row in reader:
    key = (row[1], row[3])

    if key not in entries:
        writer.writerow(row)
        entries.add(key)

    del writer #Force the writer to clean up


    df = pd.read_csv('remacos.csv', index_col="mac")         
    counteros = Counter(df['os'])
    os_names = counteros.keys()
    os_counts = counteros.values()

# Read Removed Duplicated entries Database and Count Values for Browsers.
    df = pd.read_csv('remacbrowser.csv', index_col="mac")         
    counterbrowsers = Counter(df['browser'])
    browser_names = counterbrowsers.keys()
    browser_counts = counterbrowsers.values()


#New Bar Chart with Fonts
    countdata = df['browser'].value_counts()
    ax = countdata.plot(kind='bar',                 
                        figsize=[9, 6], 
                        width=0.9, 
                        alpha=0.6, 
                        color='g',
                        edgecolor='w',
                        grid=False,
                        ylim=[0, 400])

    ax.set_xticks(range(len(countdata)))
    ax.set_xticklabels(countdata.index, rotation=45,  rotation_mode='anchor', ha='right', fontproperties=ticks_font)
    ax.yaxis.grid(True)
    for label in ax.get_yticklabels():
    label.set_fontproperties(ticks_font)

    ax.set_title('Most Popular Browsers', fontproperties=title_font)
    ax.set_xlabel('', fontproperties=label_font)
    ax.set_ylabel('Number of counts', fontproperties=label_font)

    plt.show()

# Make Pie Chart for Browsers
    plt.figure(figsize=plt.figaspect(1))
#values = sorted(browser_counts) 
#labels = browser_names

    labels, values = zip(*sorted(counterbrowsers.items(), key=lambda x: x[1])) #Counteros is the Dictionary-like object we are trying to sort. This 'zip' solves the problem of sorting each of them independently. This will sort as pairs, by value.
colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral', '#008DB8','#00AAAA','#001CF0','#00FF80', 'c','m','r','b', '#191970','#0038E2','#0055D4','#0071C6','#00E28E', '#00C69C']
#explode = (0, 0, 0, 0.1, 0.1, 0.2, 0.3, 0.4, 0.6)
explode = list()
    for k in labels:
       explode.append(0.1)
#sizes = counts.values.tolist()
    def make_autopct(values):
        def my_autopct(pct):
            total = sum(values)
            val=int(round(pct*total/100))
            return '{p:.2f}%  ({v:d})'.format(p=pct,v=val)
        return my_autopct

    pie = plt.pie(values, labels=labels, explode=explode, colors=colors,     shadow=True, startangle=90, autopct=make_autopct(values))

    bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
    arrowprops=dict(arrowstyle="-",connectionstyle="angle,angleA=0,angleB=90")
kw = dict(xycoords='data',textcoords='data',arrowprops=arrowprops, 
          bbox=bbox_props, zorder=0, va="center")

    for i, p in enumerate(pie):
        ang = (p.theta2 - p.theta1)/2.+p.theta1
        y = np.sin(ang/180.*np.pi)
        x = 1.35*np.sign(np.cos(ang/180.*np.pi))
        plt.gca().annotate(str(1+i), xy=(0, 0), xytext=( x, y), **kw )

    plt.legend(labels, loc="lower right")
    plt.axis('equal') # Set aspect ratio to be equal so that pie is drawn as a circle, # View the plot drop above
plt.title('Browsers Analytics', bbox={'facecolor':'0.8', 'pad':5})
plt.show()

The CSV files are displayed in a table like this: One CSV compares Mac address with Browser The other CSV links Mac and OS for the count data. In the pie chart I am just trying to get one to show I can then replicate the results (hopefully).

id                 mac              os         browser                                                1        fe:fe:fe:fe:fe:fe    Windows 10     Chrome 
2          fe:fe:fe:fe:fe:fe    Mac OS X       Chrome 
3          fe:fe:fe:fe:fe:fe    iPhone         Handheld Browser
5          37:02:e6:87:FF:77    Windows 8.1    Internet Explorer 

The bar chart is just one way of showing the data however displaying the data in a pie chart with callouts is the goal. I'm having trouble implemnting a solution to overlap/callout the labels. The value using here for i, p in enumerate(pie) is where i seem to be getting the errors like AttributeError: 'list' object has no attribute 'theta2'

Traceback (most recent call last):
  File "csv-test.py", line 176, in <module>
    ang = (p.theta2 - p.theta1)/2.+p.theta1
AttributeError: 'list' object has no attribute 'theta2'

If i change the (pie) value to (counterbrowser) I get:

Traceback (most recent call last):
  File "csv-test.py", line 176, in <module>
    ang = (p.theta2 - p.theta1)/2.+p.theta1
AttributeError: 'str' object has no attribute 'theta2'

Patches = pie gives me the following error (replacing (pie) with patches)

Traceback (most recent call last):
  File "csv-test.py", line 176, in <module>
    ang = (p.theta2 - p.theta1)/2.+p.theta1
AttributeError: 'list' object has no attribute 'theta2'
iHaag
  • 65
  • 2
  • 10
  • The code in the question is not reproducible. The linked answer, which you haven't actually accepted yet, shows how to produce such labels. If you have problems implementing this solution, you need to provide a [mcve] of the issue and the complete errortraceback. – ImportanceOfBeingErnest Jun 28 '17 at 13:16
  • remacos.csv and remacbrowser.csv format is like shown below. I had to do this to remove duplicates.... Ive tried what I can to make these callouts they just never work with my data, either 'too many values to unpack', typeError or AttributeError Here is the table layout, gives the example of the data im working with.... id mac os browser 1 fe:fe:fe:fe:fe:fe Windows 10 Chrome 2 fe:fe:fe:fe:fe:fe Mac OS X Chrome 3 fe:fe:fe:fe:fe:fe iPhone Handheld Browser 5 37:02:e6:87:FF:77 Windows 8.1 Internet Explorer Sorry dont know how to show tables in Comments.... – iHaag Jun 28 '17 at 13:23
  • Please [edit] your question, instead of instead of putting unreadable text in the comments. "They just never work with my data" is not a sufficient problem description. Include the full error traceback. Again, even showing the file format does not make this a [mcve]. – ImportanceOfBeingErnest Jun 28 '17 at 13:33
  • To regenerate the problem, sample tables were supplied, unreadable because I could display it as a table. The output error is at this line: for i, p in enumerate(pie): the value Pie gives AttributeError: 'list' object has no attribute 'theta2' changing the value from Pie to counterdata it gives the other error listed. Either way, I'm struggling to show call out labels with my data and would really appreciate an example with such data as im at a loss.... – iHaag Jun 28 '17 at 13:38
  • The bar chart is a comparison same data. – iHaag Jun 28 '17 at 13:48
  • I think you misunderstand the role of comments. Why don't you [edit] your question? :-( – ImportanceOfBeingErnest Jun 28 '17 at 13:48
  • The question has been edited, hopefully, this is easier to understand & replicate now? – iHaag Jun 28 '17 at 14:04
  • In the linked question's answer it clearly says `i, p in enumerate(patches)` not `i, p in enumerate(pie)`. – ImportanceOfBeingErnest Jun 28 '17 at 14:09
  • I get the same error, so I tried to alter it for my code., if I add, texts to patches so patches, texts = pie = plt.pie(... I get the error: ValueError: too many values to unpack that is with i, p in enumerate(patches) – iHaag Jun 28 '17 at 14:18

1 Answers1

0

This is simply a matter of typing the solution in correctly; and you forgot the ,texts part.
The linked question's answer states

patches, texts = pie = plt.pie(total, startangle=5)
# ...
for i, p in enumerate(patches):
    # ...

If you have autopercentage activated, you need 3 values to unpack

patches, texts, autotexts = plt.pie(..., autopct=..)
# ...
    for i, p in enumerate(patches):
        # ...

Alternatively you may of course not unpack the pie's return and use

pie = plt.pie(total, startangle=5)
# ...
for i, p in enumerate(pie[0]):
   # ...
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • pie[0] gave me callouts but numbers, not labels..... they also overlap.. if I do patches, texts I get the "Too many values to unpack" callback. – iHaag Jun 28 '17 at 14:26
  • Ok so you already found a solution which does not produce the error and only showed some undersired behaviour. I think you're mixing a lot of different issues here. (a) an error (b) undesired labels (c) overlapping labels. Those problems are completely unrelates and you would need to decide which problem you want to solve and specifically ask for that one. – ImportanceOfBeingErnest Jun 28 '17 at 14:40
  • Thank you, ideally I'd like to solve all 3 issues... can't work out how it's now giving numbers.. – iHaag Jun 28 '17 at 14:42
  • If this was your code, how would you generate the labels as callouts and make sure they do not overlap? – iHaag Jun 28 '17 at 14:44
  • In your call to annotate you ask for numbers: `annotate(str(1+i), xy=(0, 0), xytext=( x, y), **kw )`. If you want labels, you need to change the first argument to the label you want to provide. Concerning overlapping there is not automated way, so it would depend on the actual labels and their length and I would therefore start by producing a [mcve] of the issue. – ImportanceOfBeingErnest Jun 28 '17 at 14:47