still_learning
still_learning

Reputation: 806

How to plot a dictionary

I have some problems to plot the following values:

my_dict={'word1': ['31', '131', '2'], 'word2': ['42', '33', '154', '21']}

What I have done is

plt.bar(my_dict.keys(), my_dict.values(), color='g')

but I got this error:

TypeError: ufunc 'add' did not contain a loop with signature matching types dtype('

Then I have tried with

plt.plot(*zip(*sorted(my_dict.items())))
plt.show()

but I got this other error:

TypeError: unhashable type: 'list'

I would be interested in frequency.

What should I do to fix it?

From the original dataset (as I have got some error to replicate the code):

my_dict = defaultdict(list)

print({ k : v for k, v in my_dict.items() })

output:

{'word1': ['122', '121.2', '132', '132', '144', '144.5', '144', '150', '150,5', '150,5', '150,5'], 'word2': ['230', '230', '230', '230'], 'word3': ['542', '542', '540'], 'word4': ['134', '134']}

I would need to plot the frequency of values in each word (for example, for word1 I should have a frequency of 2 for 132 and 144, then 3 for 150.5, 1 for all the other values).

Upvotes: 1

Views: 4174

Answers (2)

Trenton McKinney
Trenton McKinney

Reputation: 62393

Use pandas and zip_longest

  • Pandas requires the columns to have the same length, so zip_longest will fill blanks with None.
  • There are a number of options to shape the data, based upon how you want it plotted.
import pandas as pd
from itertools import zip_longest
import matplotlib.pyplot as plt

# data
d = {'word1': ['122', '121.2', '132', '132', '144', '144.5', '144', '150', '150.5', '150.5', '150.5'], 'word2': ['230', '230', '230', '230'], 'word3': ['542', '542', '540'], 'word4': ['134', '134']}

# since the values lists are uneven
cols = d.keys()
val = list(zip_longest(*d.values()))

# dataframe
df = pd.DataFrame(val, columns=cols, dtype=float)

    word1  word2  word3  word4
0   122.0  230.0  542.0  134.0
1   121.2  230.0  542.0  134.0
2   132.0  230.0  540.0    NaN
3   132.0  230.0    NaN    NaN
4   144.0    NaN    NaN    NaN
5   144.5    NaN    NaN    NaN
6   144.0    NaN    NaN    NaN
7   150.0    NaN    NaN    NaN
8   150.5    NaN    NaN    NaN
9   150.5    NaN    NaN    NaN
10  150.5    NaN    NaN    NaN

plot with annotations

ax = df.plot.bar()

f = [df[c].value_counts().to_dict() for c in df.columns]  # list of list of value counts
f = dict(kv for d in f for kv in d.items())  # this will break if the values for each word aren't unique

for p in ax.patches:

    if p.get_height() > 0:

        # add value at top of bar
        ax.annotate(format(p.get_height(), '.1f'),
                    (p.get_x() + p.get_width() / 2., p.get_height() + 10),
                    ha = 'center', va = 'center', fontsize=9, rotation=90,
                    xytext = (0, 10), textcoords = 'offset points')

        # add frequency of value at center of bar
        ax.annotate(format(f[p.get_height()], '.0f'),
            (p.get_x() + p.get_width() / 2., p.get_height() / 2),
            ha = 'center', va = 'center', fontsize=9, rotation=0,
            xytext = (0, 10), textcoords = 'offset points')

enter image description here

tdf = df.T  # transpose dataframe df

ax = tdf.plot.bar()

f = [df[c].value_counts().to_dict() for c in df.columns]  # list of list of value counts
f = dict(kv for d in f for kv in d.items())  # this will break if the values for each word aren't unique

for p in ax.patches:

    if p.get_height() > 0:

        # add value at top of bar
        ax.annotate(format(p.get_height(), '.1f'),
                    (p.get_x() + p.get_width() / 2., p.get_height() + 10),
                    ha = 'center', va = 'center', fontsize=9, rotation=90,
                    xytext = (0, 10), textcoords = 'offset points')

        # add frequency of value at center of bar
        ax.annotate(format(f[p.get_height()], '.0f'),
            (p.get_x() + p.get_width() / 2., p.get_height() / 2),
            ha = 'center', va = 'center', fontsize=9, rotation=0,
            xytext = (0, 10), textcoords = 'offset points')

enter image description here

Without annotations

  • Coloring by hue places the bars off-center based upon the number of unique values in the column used by hue, word in this case.
    • In the example below, all four words contain the value 150.5, so you can see them grouped in the plot.
  • The bars are horizontal to accommodate a large number of values.
    • Just increase the figsize height.
import seaborn as sns

d = {'word1': ['122', '121.2', '132', '132', '144', '144.5', '144', '150', '150.5', '150.5', '150.5'], 'word2': ['230', '230', '230', '230', '150.5'], 'word3': ['542', '542', '540', '150.5'], 'word4': ['134', '134', '150.5']}

cols = d.keys()
val = list(zip_longest(*d.values()))

# dataframe
df = pd.DataFrame(val, columns=cols, dtype=float)

# convert from wide to long
df['id'] = df.index
dfl = pd.wide_to_long(df, stubnames='word', j='x', i='id').reset_index().rename(columns={'word': 'v', 'x': 'word'}).dropna()

# groupby for frequency counts
dflg = dfl.groupby('word').agg({'v': 'value_counts'}).rename(columns={'v': 'freq_count'}).reset_index().sort_values('v')

# plot
plt.figure(figsize=(6, 10))
p = sns.barplot(y='v', x='freq_count', data=dflg, hue='word', orient='h')

enter image description here

Upvotes: 1

Red
Red

Reputation: 27557

Here is how you can do that with matplotlib:

import matplotlib.pyplot as plt
from numpy import random

mydict={'word1': ['122', '121.2', '132', '132', '144', '144.5', '144', '150', '150,5', '150,5', '150,5'], 'word2': ['230', '230', '230', '230'], 'word3': ['542', '542', '540'], 'word4': ['134', '134']}


for k,l in mydict.items():
    labeled = False
    c=random.rand(3,)
    for v in l:
        if labeled:
            plt.bar(v,len([d for d in l if d==v]),color=c)
        else:
            plt.bar(v,len([d for d in l if d==v]),label=k,color=c)
            labeled = True

plt.legend()
plt.show()

enter image description here

Upvotes: 2

Related Questions