I have a data that has several columns in it.
Country Weight # of food/day ....
---------------------------------------------
USA 180 4
China 190 12
USA 150 2
Canada 300 10
I want to create (separate) histogram for each of the columns such that histogram_1 shows the distribution of 'Country', histogram_2 shows the distribution of 'Weight', etc.
I'm currently using panda to upload and manipulate the data.
Is the easy way to do this is by doing like this?
for column in df:
plt.hist(column)
plt.show()
Please forgive me if my idea sounds so stupid.
Any help would be highly appreciated, thanks!
Defining a histogram for non-numeric or discrete values is not unambiguous. Often the question is "how many item of each unique kind are there?". This can be achieved through .value_counts. Since you say "# of histograms == # of columns (features)", we might create one subplot per column.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({"Countries" : ["USA", "Mexico", "Canada", "USA", "Mexico"],
"Weight" : [180, 120, 100, 120, 130],
"Food" : [2,2,2,4,2]})
fig, axes = plt.subplots(ncols=len(df.columns), figsize=(10,5))
for col, ax in zip(df, axes):
df[col].value_counts().sort_index().plot.bar(ax=ax, title=col)
plt.tight_layout()
plt.show()
Can use this instead of for loop, histograms for all numeric columns will be generated!
df.hist(bins=10, figsize=(25, 20))
If you want the histograms in different windows, then you can do in this way:
df.set_index('Country', inplace=True)
for col in df.columns:
df[col].plot.bar()
plt.show()
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({"Countries" : ["USA", "Mexico", "Canada", "USA", "Mexico"],
"Weight" : [200, 150, 190, 60, 40],
"Food" : [2,6,4,4,6]})
for col in df.columns:
plt.hist(df[col])
plt.xlabel(col)
plt.show()
Related
I'm trying to combine seaborn's heatmap and kdeplot in one figure, but so far the result is not very promising since I cannot find a way to make them overlap. As a result, the heatmap is just squeezed to the left side of the figure.
I think the reason is that seaborn doesn't seem to recognize the x-axis as the same one in two charts (see picture below), although the data points are exactly the same. The only difference is that for heatmap I needed to pivot them, while for the kdeplot pivoting is not needed.
Therefore, data for the axis are coming from the same dataset, but in the different forms as it can be seen in the code below.
The dataset sample looks something like this:
X Y Z
7,75 280 52,73
3,25 340 54,19
5,75 340 53,61
2,5 180 54,67
3 340 53,66
1,75 340 54,81
4,5 380 55,18
4 240 56,49
4,75 380 55,17
4,25 180 55,40
2 420 56,42
2,25 380 54,90
My code:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
f, ax = plt.subplots(figsize=(11, 9), dpi=300)
plt.tick_params(bottom='on')
# dataset is just a pandas frame with data
X1 = dataset.iloc[:, :3].pivot("X", "Y", "Z")
X2 = dataset.iloc[:, :2]
ax = sns.heatmap(X1, cmap="Spectral")
ax.invert_yaxis()
ax2 = plt.twinx()
sns.kdeplot(X2.iloc[:, 1], X2.iloc[:, 0], ax=ax2, zorder=2)
ax.axis('tight')
plt.show()
Please help me with placing kdeplot on top of the heatmap. Ideally, I would like my final plot to look something like this:
Any tips or hints will be greatly appreciated!
The question can be a bit hard to understand, because the dataset can't be "just some data". The X and Y values need to lie on a very regular grid. No X,Y combination can be repeated, but not all values appear. The kdeplot will then show where the used values of X,Y are concentrated.
Such a dataset can be simulated by first generating dummy data for a full grid, and then take a subset.
Now, a seaborn heatmap uses categorical X and Y axes. Such axes are very hard to align with the kdeplot. To obtain a similar heatmap with numerical axes, ax.pcolor() can be used.
from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
xs = np.arange(2, 10, 0.25)
ys = np.arange(150, 400, 10)
# first create a dummy dataset over a full grid
dataset = pd.DataFrame({'X': np.repeat(xs, len(ys)),
'Y': np.tile(ys, len(xs)),
'Z': np.random.uniform(50, 60, len(xs) * len(ys))})
# take a random subset of the rows
dataset = dataset.sample(200)
fig, ax = plt.subplots(figsize=(11, 9), dpi=300)
X1 = dataset.pivot("X", "Y", "Z")
collection = ax.pcolor(X1.columns, X1.index, X1, shading='nearest', cmap="Spectral")
plt.colorbar(collection, ax=ax, pad=0.02)
# default, cut=3, which causes a lot of surrounding whitespace
sns.kdeplot(x=dataset["Y"], y=dataset["X"], cut=1.5, ax=ax)
fig.tight_layout()
plt.show()
In this data set I need to plot,pH as the x-column which is having continuous data and need to group it together the pH axis as per the quality value and plot the histogram. In many of the resources I referred I found solutions for using random data generated. I tried this piece of code.
plt.hist(, density=True, bins=1)
plt.ylabel('quality')
plt.xlabel('pH');
Where I eliminated the random generated data, but I received and error
File "<ipython-input-16-9afc718b5558>", line 1
plt.hist(, density=True, bins=1)
^
SyntaxError: invalid syntax
What is the proper way to plot my data?I want to feed into the histogram not randomly generated data, but data found in the data set.
Your Error
The immediate problem in your code is the missing data to the plt.hist() command.
plt.hist(, density=True, bins=1)
should be something like:
plt.hist(data_table['pH'], density=True, bins=1)
Seaborn histplot
But this doesn't get the plot broken down by quality. The answer by Mr.T looks correct, but I'd also suggest seaborn which works with "melted" data like you have. The histplot command should give you what you want:
import seaborn as sns
sns.histplot(data=df, x="pH", hue="quality", palette="Dark2", element='step')
Assuming the table you posted is in a pandas.DataFrame named df with columns "pH" and "quality", you get something like:
The palette (Dark2) can can be any matplotlib colormap.
Subplots
If the overlaid histograms are too hard to see, an option is to do facets or small multiples. To do this with pandas and matplotlib:
# group dataframe by quality values
data_by_qual = df.groupby('quality')
# create a sub plot for each quality group
fig, axes = plt.subplots(nrows=len(data_by_qual),
figsize=[6,12],
sharex=True)
fig.subplots_adjust(hspace=.5)
# loop over axes and quality groups together
for ax, (quality, qual_data) in zip(axes, data_by_qual):
ax.hist(qual_data['pH'], bins=10)
ax.set_title(f"quality = {quality}")
ax.set_xlabel('pH')
Altair Facets
The plotting library altair can do this for you:
import altair as alt
alt.Chart(df).mark_bar().encode(
alt.X("pH:Q", bin=True),
y='count()',
).facet(row='quality')
Several possibilities here to represent multiple histograms. All have in common that the data have to be transformed from long to wide format - meaning, each category is in its own column:
import matplotlib.pyplot as plt
import pandas as pd
#test data generation
import numpy as np
np.random.seed(123)
n=300
df = pd.DataFrame({"A": np.random.randint(1, 100, n), "pH": 3*np.random.rand(n), "quality": np.random.choice([3, 4, 5, 6], n)})
df.pH += df.quality
#instead of this block you have to read here your stored data, e.g.,
#df = pd.read_csv("my_data_file.csv")
#check that it read the correct data
#print(df.dtypes)
#print(df.head(10))
#bringing the columns in the required wide format
plot_df = df.pivot(columns="quality")["pH"]
bin_nr=5
#creating three subplots for different ways to present the same histograms
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(6, 12))
ax1.hist(plot_df, bins=bin_nr, density=True, histtype="bar", label=plot_df.columns)
ax1.legend()
ax1.set_title("Basically bar graphs")
plot_df.plot.hist(stacked=True, bins=bin_nr, density=True, ax=ax2)
ax2.set_title("Stacked histograms")
plot_df.plot.hist(alpha=0.5, bins=bin_nr, density=True, ax=ax3)
ax3.set_title("Overlay histograms")
plt.show()
Sample output:
It is not clear, though, what you intended to do with just one bin and why your y-axis was labeled "quality" when this axis represents the frequency in a histogram.
I have the code below which overlays a density curve on a histogram. It does this for the ‘Fresh’ field in my data, which is a continuous field. I would like to create similar plots filtering by the unique values in the ‘Channel’ field. For example in pandas to create histograms similar to what I'm trying to accomplish I would use:
data_df.hist(column=‘Fresh’,by=‘Channel’)
Can anyone suggest how to do something similar for the seaborn code below?
code:
import seaborn as sns
sns.distplot(data_df[‘Fresh’], hist=True, kde=True,
bins=int(data_df.shape[0]/5), color = 'darkblue',
hist_kws={'edgecolor':'black'},
kde_kws={'linewidth': 4})
data
Channel Fresh
0 2 12669
1 2 7057
2 2 6353
3 1 13265
4 2 22615
5 2 9413
6 2 12126
7 2 7579
8 1 5963
9 2 6006
I think the Seaborn way is to create a FacetGrid, and then to map an axis-level plotting function onto it. In your case:
g = sns.FacetGrid(data_df, col='Channel', margin_titles=True)
g.map(sns.distplot,
'Fresh',
bins=int(data_df.shape[0]/5),
color='darkblue',
hist_kws={'edgecolor': 'black'},
kde_kws={'linewidth': 4});
Check out the docs for more: https://seaborn.pydata.org/tutorial/axis_grids.html
Alternatively, you can groupby your DataFrame based on the Channel and then plot the two groups in different subplots
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
data_df = pd.DataFrame({'Channel': [2, 2, 2, 1, 2, 2, 2, 2, 1, 2],
'Fresh': [12669, 7057, 6353, 13265, 22615,
9413, 12126, 7579, 5963,6006]})
df1 = data_df.groupby('Channel')
fig, axes = plt.subplots(nrows=1, ncols=len(df1), figsize=(10, 3))
for ax, df in zip(axes.flatten(), df1.groups):
sns.distplot(df1.get_group(df)['Fresh'], hist=True, kde=True,
bins=int(data_df.shape[0]/5), color = 'darkblue',
hist_kws={'edgecolor':'black'},
kde_kws={'linewidth': 4}, ax=ax)
plt.tight_layout()
color = []
for key,value in ms.iterrows():
if(value['Color']=='Blue'):
color.append('b')
elif(value['Color']=='Green'):
color.append('g')
elif(value['Color']=='Red'):
color.append('r')
elif(value['Color']=='Yellow'):
color.append('y')
elif(value['Color']=='Orange'):
color.append('o')
else:
color.append('k')
ax =ms[['Height','Color']].plot(x='Color', kind='bar', title="Correlation",
figsize=(15,10), color=color legend=True, fontsize=12)
ax.set_xlabel("Colors", fontsize=12)
ax.set_ylabel("Height", fontsize=12)
My intention is to plot a bar graph that shows Color against Height. I managed to do it. However, I would like each of the bars to show respective color. In accord with the data set, I would like the 1st bar to show red...and so on. I tried adding the color, but it still shows only 1 color.
The trick is to create a multicolumn dataframe and use the stacked=True option.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame({"Height" : [5,3,6,4],
"Color" : ["Blue", "Green", "Red", "Yellow"]})
color = []
for key,value in df.iterrows():
if(value['Color']=='Blue'):
color.append('b')
elif(value['Color']=='Green'):
color.append('g')
elif(value['Color']=='Red'):
color.append('r')
elif(value['Color']=='Yellow'):
color.append('y')
elif(value['Color']=='Orange'):
color.append('o')
else:
color.append('k')
df2 = pd.DataFrame(np.diag(df["Height"]), columns=df["Color"], index=df["Color"])
ax = df2.plot(kind='bar', title="Correlation", color=color, legend=True,
fontsize=12, stacked=True)
ax.set_xlabel("Colors", fontsize=12)
ax.set_ylabel("Height", fontsize=12)
plt.show()
You do not have to create if/else conditions:
import pandas as pd
df = pd.DataFrame({"Height" : [5,3,6,4],
"Color" : ["Blue", "Green", "Red", "Yellow"]})
df.set_index('Color').Height.plot(kind='bar',color=df.Color)
considering the following pandas DataFrame:
labels values_a values_b values_x values_y
0 date1 1 3 150 170
1 date2 2 6 200 180
It is easy to plot this with Seaborn (see example code below). However, due to the big difference between values_a/values_b and values_x/values_y, the bars for values_a and values_b are not easily visible (actually, the dataset given above is just a sample and in my real dataset the difference is even bigger). Therefore, I would like to use two y-axis, i.e., one y-axis for values_a/values_b and one for values_x/values_y. I tried to use plt.twinx() to get a second axis but unfortunately, the plot shows only two bars for values_x and values_y, even though there are at least two y-axis with the right scaling. :) Do you have an idea how to fix that and get four bars for each label whereas the values_a/values_b bars relate to the left y-axis and the values_x/values_y bars relate to the right y-axis?
Thanks in advance!
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
("date2", 2, 6, 200, 180)],\
columns=columns)
# working example but with unreadable values_a and values_b
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
var_name="source", value_name="value_numbers")
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
data=test_data_melted)
plt.show()
# values_a and values_b are not displayed
values1_melted = pd.melt(test_data, id_vars=columns[0],\
value_vars=["values_a", "values_b"],\
var_name="source1", value_name="value_numbers1")
values2_melted = pd.melt(test_data, id_vars=columns[0],\
value_vars=["values_x", "values_y"],\
var_name="source2", value_name="value_numbers2")
g1 = sns.barplot(x=columns[0], y="value_numbers1", hue="source1",\
data=values1_melted)
ax2 = plt.twinx()
g2 = sns.barplot(x=columns[0], y="value_numbers2", hue="source2",\
data=values2_melted, ax=ax2)
plt.show()
This is probably best suited for multiple sub-plots, but if you are truly set on a single plot, you can scale the data before plotting, create another axis and then modify the tick values.
Sample Data
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import numpy as np
columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
("date2", 2, 6, 200, 180)],\
columns=columns)
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
var_name="source", value_name="value_numbers")
Code:
# Scale the data, just a simple example of how you might determine the scaling
mask = test_data_melted.source.isin(['values_a', 'values_b'])
scale = int(test_data_melted[~mask].value_numbers.mean()
/test_data_melted[mask].value_numbers.mean())
test_data_melted.loc[mask, 'value_numbers'] = test_data_melted.loc[mask, 'value_numbers']*scale
# Plot
fig, ax1 = plt.subplots()
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
data=test_data_melted, ax=ax1)
# Create a second y-axis with the scaled ticks
ax1.set_ylabel('X and Y')
ax2 = ax1.twinx()
# Ensure ticks occur at the same positions, then modify labels
ax2.set_ylim(ax1.get_ylim())
ax2.set_yticklabels(np.round(ax1.get_yticks()/scale,1))
ax2.set_ylabel('A and B')
plt.show()