0
votes

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'
1
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 minimal reproducible example of the issue and the complete errortraceback. - ImportanceOfBeingErnest
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
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 minimal reproducible example. - ImportanceOfBeingErnest
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
The bar chart is a comparison same data. - iHaag

1 Answers

0
votes

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]):
   # ...