*Edit:
Why the right plot (Bar) is showing 50% , half black screen on the plot, wierd numbers, "garbage"... how to fix the right plot ?
here is my code:
top_series = all_data.head(50).groupby('Top Rated ')['Top Rated '].count()
top_values = top_series.values.tolist()
top_index = ['Top Rated', 'Not Top Rated']
top_colors = ['#27AE60', '#E74C3C']
rating_series = all_data.head(50).groupby('Rating')['Rating'].count()
rating_values = rating_series.values.tolist()
rating_index = ['High' , 'Low']
rating_colors = ['#F1C40F', '#27AE60']
fig, axs = plt.subplots(1,2, figsize=(16,5))
axs[0].pie(top_values, labels=top_index, autopct='%1.1f%%', shadow=True, startangle=90,
explode=(0.05, 0.05), radius=1.5, colors=top_colors, textprops={'fontsize':15})
axs[1].bar(rating_series.index, rating_series.values, color='b')
axs[1].set_xlabel('Rating')
axs[1].set_ylabel('Amount')
fig.suptitle('Does "Rating" really affect on Top Sellers ? ')
CSV cols:
Output (look at the right plot):
I suppose, that keys is a list of all keys. So it can have a different shape than the top_values.
If you would do:
axs[1].bar(top_series.index, top_series.values, color='b')
It should work well.
But, if you just want to plot the histogram, there is even shorter version, without temporary objects:
all_data['Top Rated '].value_counts().plot(kind = 'bar', ax=axs[1])
Edit: The Rating column is a numeric one, not a string one. You have to create a column which will have values High and Low. For example:
all_data['Rating_Cat'] = all_data['Rating'].apply(lambda x : 'High' if (x > 10000000 ) else 'Low')
And then use this column to plot this kind of bar plot
Related
I have a DataFrame like below. It has Actual and Predicted columns. I want to compare Actual Vs Predicted in Bar plot in one on one. I have confidence value for Predicted column and default for Actual confidence is 1. So, I want to keep Each row in single bar group Actual and Predicted value will be X axis and corresponding Confidence score as y value.
I am unable to get the expected plot because X axis values are not aligned or grouped to same value in each row.
Actual Predicted Confidence
0 A A 0.90
1 B C 0.30
2 C C 0.60
3 D D 0.75
Expected Bar plot.
Any hint would be appreciable. Please let me know if further details required.
What I have tried so far.
df_actual = pd.DataFrame()
df_actual['Key']= df['Actual'].copy()
df_actual['Confidence'] = 1
df_actual['Identifier'] = 'Actual'
df_predicted=pd.DataFrame()
df_predicted = df[['Predicted', 'Confidence']]
df_predicted = df_predicted.rename(columns={'Predicted': 'Key'})
df_predicted['Identifier'] = 'Predicted'
df_combined = pd.concat([df_actual,df_predicted], ignore_index=True)
df_combined
fig = px.bar(df_combined, x="Key", y="Confidence", color='Identifier',
barmode='group', height=400)
fig.show()
I have found that adjusting the data first makes it easier to get the plot I want. I have used Seaborn, hope that is ok. Please see if this code works for you. I have considered that the df mentioned above is already available. I created df2 so that it aligns to what you had shown in the expected figure. Also, I used index as the X-axis column so that the order is maintained... Some adjustments to ensure xtick names align and the legend is outside as you wanted it.
Code
vals= []
conf = []
for x, y, z in zip(df.Actual, df.Predicted, df.Confidence):
vals += [x, y]
conf += [1, z]
df2 = pd.DataFrame({'Values': vals, 'Confidence':conf}).reset_index()
ax=sns.barplot(data = df2, x='index', y='Confidence', hue='Values',dodge=False)
ax.set_xticklabels(['Actual', 'Predicted']*4)
plt.legend(bbox_to_anchor=(1.0,1))
plt.show()
Plot
Update - grouping Actual and Predicted bars
Hi #Mohammed - As we have already used up hue, I don't think there is a way to do this easily with Seaborn. You would need to use matplotlib and adjust the bar position, xtick positions, etc. Below is the code that will do this. You can change SET1 to another color map to change colors. I have also added a black outline as the same colored bars were blending into one another. Further, I had to rotate the xlables, as they were on top of one another. You can change it as per your requirements. Hope this helps...
vals = df[['Actual','Predicted']].melt(value_name='texts')['texts']
conf = [1]*4 + list(df.Confidence)
ident = ['Actual', 'Predicted']*4
df2 = pd.DataFrame({'Values': vals, 'Confidence':conf, 'Identifier':ident}).reset_index()
uvals, uind = np.unique(df2["Values"], return_inverse=1)
cmap = plt.cm.get_cmap("Set1")
fig, ax=plt.subplots()
l = len(df2)
pos = np.arange(0,l) % (l//2) + (np.arange(0,l)//(l//2)-1)*0.4
ax.bar(pos, df2["Confidence"], width=0.4, align="edge", ec="k",color=cmap(uind) )
handles=[plt.Rectangle((0,0),1,1, color=cmap(i), ec="k") for i in range(len(uvals))]
ax.legend(handles=handles, labels=list(uvals), prop ={'size':10}, loc=9, ncol=8)
pos=pos+0.2
pos.sort()
ax.set_xticks(pos)
ax.set_xticklabels(df2["Identifier"][:l], rotation=45,ha='right', rotation_mode="anchor")
ax.set_ylim(0, 1.2)
plt.show()
Output plot
I updated #Redox answer to get the exact output.
df_ = pd.DataFrame({'Labels': df.reset_index()[['Actual', 'Predicted', 'index']].values.ravel(),
'Confidence': np.array(list(zip(np.repeat(1, len(df)), df['Confidence'].values, np.repeat(0, len(df))))).ravel()})
df_.loc[df_['Labels'].astype(str).str.isdigit(), 'Labels'] = ''
plt.figure(figsize=(15, 6))
ax=sns.barplot(data = df_, x=df_.index, y='Confidence', hue='Labels',dodge=False, ci=None)
ax.set_xticklabels(['Actual', 'Predicted', '']*len(df))
plt.setp(ax.get_xticklabels(), rotation=90)
ax.tick_params(labelsize=14)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
Output:
Removed loop to improve performance
Added blank bar values to look alike group chart.
I am trying to create subplots: first Pie plot (got it), second bar plot (didn't succeed):
These are the columns:
My Code:
top_series = all_data.head(50).groupby('Top Rated ')['Top Rated '].count()
top_values = top_series.values.tolist()
top_index = ['Top Rated', 'Not Top Rated']
top_colors = ['#27AE60', '#E74C3C']
all_data['Rating_Cat'] = all_data['Rating'].apply(lambda x : 'High' if (x > 10000000 ) else 'Low')
rating_series = all_data.head(50).groupby('Rating_Cat')['Rating_Cat'].count()
rating_values = rating_series.values.tolist()
rating_index = ['High' , 'Low']
rating_colors = ['#F1C40F', '#27AE60']
fig, axs = plt.subplots(1,2, figsize=(16,5))
axs[0].pie(top_values, labels=top_index, autopct='%1.1f%%', shadow=True, startangle=90,
explode=(0.05, 0.05), radius=1.2, colors=top_colors, textprops={'fontsize':12})
all_data['Rating_Cat'].value_counts().plot(kind = 'bar', ax=axs[1])
fig.suptitle('Does "Rating" really affect on Top Sellers ?' , fontsize=17)
My question:
How to create the second plot that will get output like:
axis X = 1 , 2 , 3 , 4 .... 50 + Top reated / NO (according to the current col)
axis y = the rating from 0 to 7603388.0
I have really tried lots of things, but I am kind of lost here... please help !!
In first plot you are taking first 50 rows of the dataset and plot shares of each value in Top Rated column.
If I understand what you are trying to do in second plot (You want to have each of the Rating from first 100 values plotted from first to last with color based on the Top rated):
#taking first 100 rows
rating_series = all_data.head(100).copy()
#assigning color to the values, so you could use it in bar() plot
rating_series["color"] = rating_series["Top Rated "].map({"Top Rated": "#27AE60", "No": "#E74C3C"})
#plotting the values
axs[1].bar(rating_series.index, rating_series["Rating"], color = rating_series["color"])
If you want to add legend to the plot, you have to do it manually
import matplotlib.patches as mpatches
axs[1].legend(handles=[mpatches.Patch(color='#27AE60', label='Top Rated'),
mpatches.Patch(color='#E74C3C', label='Not Top Rated')])
Edit: My whole code
import pandas as pd
import numpy as np
import matplotlib.patches as mpatches
import random
df = pd.DataFrame(
{
"Rating": np.random.randint(0,7603388,size=200),
"Top Rated ": [random.choice(['Top Rated', 'No']) for rated in range(0,200)]
}
)
#taking first 100 rows
rating_series = df.head(100).copy()
#assigning color to the values, so you could use it in bar() plot
rating_series["color"] = rating_series["Top Rated "].map({"Top Rated": "#27AE60", "No": "#E74C3C"})
#checking if there were no NaNs
rating_series["color"].value_counts(dropna=False)
#Output:
#E74C3C 53
#FFC300 47
#Name: color, dtype: int64
#1st plot
top_series = rating_series.groupby('Top Rated ')['Top Rated '].count()
top_index = ['Top Rated', 'Not Top Rated']
top_colors = ['#27AE60', '#E74C3C']
fig, axs = plt.subplots(1,2, figsize=(16,5))
axs[0].pie(top_series.values, labels=top_index, autopct='%1.1f%%', shadow=True, startangle=90,
explode=(0.05, 0.05), radius=1.2, colors=top_colors, textprops={'fontsize':12})
#2nd plot
axs[1].bar(rating_series.index, rating_series["Rating"], color = rating_series["color"])
axs[1].legend(handles=[mpatches.Patch(color='#27AE60', label='Top Rated'),
mpatches.Patch(color='#E74C3C', label='Not Top Rated')])
I was trying to reproduce this plot with Matplotlib:
So, by looking at the documentation, I found out that the closest thing is a grouped bar chart. The problem is that I have a different number of "bars" for each category (subject, illumination, ...) compared to the example provided by matplotlib that instead only has 2 classes (M, F) for each category (G1, G2, G3, ...). I don't know exactly from where to start, does anyone here has any clue? I think in this case the trick they made to specify bars location:
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')
does not work at all as in the second class (for example) there is a different number of bars. It would be awesome if anyone could give me an idea. Thank you in advance!
Supposing the data resides in a dataframe, the bars can be generated by looping through the categories:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# first create some test data, similar in structure to the question's
categories = ['Subject', 'Illumination', 'Location', 'Daytime']
df = pd.DataFrame(columns=['Category', 'Class', 'Value'])
for cat in categories:
for _ in range(np.random.randint(2, 7)):
df = df.append({'Category': cat,
'Class': "".join(np.random.choice([*'tuvwxyz'], 10)),
'Value': np.random.uniform(10, 17)}, ignore_index=True)
fig, ax = plt.subplots()
start = 0 # position for first label
gap = 1 # gap between labels
labels = [] # list for all the labels
label_pos = np.array([]) # list for all the label positions
# loop through the categories of the dataframe
# provide a list of colors (at least as long as the expected number of categories)
for (cat, df_cat), color in zip(df.groupby('Category', sort=False), ['navy', 'orange'] * len(df)):
num_in_cat = len(df_cat)
# add a text for the category, using "axes coordinates" for the y-axis
ax.text(start + num_in_cat / 2, 0.95, cat, ha='center', va='top', transform=ax.get_xaxis_transform())
# positions for the labels of the current category
this_label_pos = np.arange(start, start + num_in_cat)
# create bars at the desired positions
ax.bar(this_label_pos, df_cat['Value'], color=color)
# store labels and their positions
labels += df_cat['Class'].to_list()
label_pos = np.append(label_pos, this_label_pos)
start += num_in_cat + gap
# set the positions for the labels
ax.set_xticks(label_pos)
# set the labels
ax.set_xticklabels(labels, rotation=30)
# optionally set a new lower position for the y-axis
ax.set_ylim(ymin=9)
# optionally reduce the margin left and right
ax.margins(x=0.01)
plt.tight_layout()
plt.show()
So, I have made a stripplot with seaborn the easiest way, with 5 different categories:
sns.set_style('whitegrid')
plt.figure(figsize=(35,20))
sns.set(font_scale = 3)
sns.stripplot(df.speed, df.routeID, hue=df.speed>50, jitter=0.2, alpha=0.5, size=10, edgecolor='black')
plt.xlabel("Speed", size=40)
plt.ylabel("route ID", size=40)
plt.title("Velocity stripplot", size=50)
Now, the thing is I want to have a different hue for each category, say speed greater than 50 kmh for first category, 30 kmh for second and so on. Is this possible? I tried to do it passing a list for hue:
hue=([("ROUTE 30">50),("ROUTE 104">0)])
but it marks: SyntaxError: invalid syntax
The thing is, I want to do it all at once (since the most obvious answer would be to plot separately) in the same plot, how can this be done?
EDIT: I followed the suggested answer. Used the same code:
plt.figure(figsize=(20,7))
my_palette = ['b' if x > 82 else 'g' for x in df.speed.values]
sns.stripplot(df.speed, df.routeID, jitter=0.2, alpha=0.5, size=8, edgecolor='black', palette = my_palette)
but didnt turned out like expected:
I dont understand what is wrong here. Any ideas?
I suggest to create separate column in df for dot color.
try this:
# INITIAL DATA
n = 1000
df = pd.DataFrame()
df['speed'] = np.random.randint(10,90,n)
df['routeID'] = np.random.choice(['ROUTE_5','ROUTE_66','ROUTE_95','ROUTE_101'], n)
# set hue indices to match your conditions
df['hue'] = 'normal' # new column with default value
df.loc[df.speed > 50, 'hue'] = 'fast'
df.loc[(df.routeID=="ROUTE_5") & (df.speed>40)|
(df.routeID=="ROUTE_66") & (df.speed>30)|
(df.routeID=="ROUTE_95") & (df.speed>60),
'hue'] = 'special'
palette = {'normal':'g','fast':'r','special':'magenta'}
sns.stripplot(x=df.speed, y=df.routeID, size=15,
hue=df.hue, palette=palette)
I want to create a bar chart of two series (say 'A' and 'B') contained in a Pandas dataframe. If I wanted to just plot them using a different y-axis, I can use secondary_y:
df = pd.DataFrame(np.random.uniform(size=10).reshape(5,2),columns=['A','B'])
df['A'] = df['A'] * 100
df.plot(secondary_y=['A'])
but if I want to create bar graphs, the equivalent command is ignored (it doesn't put different scales on the y-axis), so the bars from 'A' are so big that the bars from 'B' are cannot be distinguished:
df.plot(kind='bar',secondary_y=['A'])
How can I do this in pandas directly? or how would you create such graph?
I'm using pandas 0.10.1 and matplotlib version 1.2.1.
Don't think pandas graphing supports this. Did some manual matplotlib code.. you can tweak it further
import pylab as pl
fig = pl.figure()
ax1 = pl.subplot(111,ylabel='A')
#ax2 = gcf().add_axes(ax1.get_position(), sharex=ax1, frameon=False, ylabel='axes2')
ax2 =ax1.twinx()
ax2.set_ylabel('B')
ax1.bar(df.index,df.A.values, width =0.4, color ='g', align = 'center')
ax2.bar(df.index,df.B.values, width = 0.4, color='r', align = 'edge')
ax1.legend(['A'], loc = 'upper left')
ax2.legend(['B'], loc = 'upper right')
fig.show()
I am sure there are ways to force the one bar further tweak it. move bars further apart, one slightly transparent etc.
Ok, I had the same problem recently and even if it's an old question, I think that I can give an answer for this problem, in case if someone else lost his mind with this. Joop gave the bases of the thing to do, and it's easy when you only have (for exemple) two columns in your dataframe, but it becomes really nasty when you have a different numbers of columns for the two axis, due to the fact that you need to play with the position argument of the pandas plot() function. In my exemple I use seaborn but it's optionnal :
import pandas as pd
import seaborn as sns
import pylab as plt
import numpy as np
df1 = pd.DataFrame(np.array([[i*99 for i in range(11)]]).transpose(), columns = ["100"], index = [i for i in range(11)])
df2 = pd.DataFrame(np.array([[i for i in range(11)], [i*2 for i in range(11)]]).transpose(), columns = ["1", "2"], index = [i for i in range(11)])
fig, ax = plt.subplots()
ax2 = ax.twinx()
# we must define the length of each column.
df1_len = len(df1.columns.values)
df2_len = len(df2.columns.values)
column_width = 0.8 / (df1_len + df2_len)
# we calculate the position of each column in the plot. This value is based on the position definition :
# Specify relative alignments for bar plot layout. From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5 (center)
# http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.plot.html
df1_posi = 0.5 + (df2_len/float(df1_len)) * 0.5
df2_posi = 0.5 - (df1_len/float(df2_len)) * 0.5
# In order to have nice color, I use the default color palette of seaborn
df1.plot(kind='bar', ax=ax, width=column_width*df1_len, color=sns.color_palette()[:df1_len], position=df1_posi)
df2.plot(kind='bar', ax=ax2, width=column_width*df2_len, color=sns.color_palette()[df1_len:df1_len+df2_len], position=df2_posi)
ax.legend(loc="upper left")
# Pandas add line at x = 0 for each dataframe.
ax.lines[0].set_visible(False)
ax2.lines[0].set_visible(False)
# Specific to seaborn, we have to remove the background line
ax2.grid(b=False, axis='both')
# We need to add some space, the xlim don't manage the new positions
column_length = (ax2.get_xlim()[1] - abs(ax2.get_xlim()[0])) / float(len(df1.index))
ax2.set_xlim([ax2.get_xlim()[0] - column_length, ax2.get_xlim()[1] + column_length])
fig.patch.set_facecolor('white')
plt.show()
And the result : http://i.stack.imgur.com/LZjK8.png
I didn't test every possibilities but it looks like it works fine whatever the number of columns in each dataframe you use.