1

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).

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
still_learning
  • 776
  • 9
  • 32

2 Answers2

2

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

Community
  • 1
  • 1
Red
  • 26,798
  • 7
  • 36
  • 58
  • yes, but the axis should be inverted. I would need the frequency on the y-axis and the values of word1 and word2 on the x-axis. To distinguish them I have thought to change colour, just to see which is related to word1 and what to word2. I do not understand what there is on the x-axis. It should be a frequency plot, should it not? – still_learning Jun 19 '20 at 02:01
  • 1
    Thank you so much @Ann Zen :) – still_learning Jun 19 '20 at 21:03
1

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

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
  • Thank you @Trenton McKinney, this is what I have been looking for. Would it be possible to see frequency (count of same items) on the y-axis? Unfortunately, I can see only the last item (word2) when I plot with my original dataset. – still_learning Jun 19 '20 at 02:09
  • 1
    Thank you so much @Trenton McKinney also for your answer and time spent helping me – still_learning Jun 19 '20 at 21:03