How can I change the filled color of stacked area plot in DataFrame? - pandas

I want to change the filled color in the stacked area plots drawn with Pandas.Dataframe.
import pandas as pd
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
ax = df.plot.area(linewidth=0);
The area plot example
Now I guess that the instance return by the plot function offers the access to modifying the attributes like colors.
But the axes classes are too complicated to learn fast. And I failed to find similar questions in the Stack Overflow.
So can any master do me a favor?

Use 'colormap' (See the document for more details):
ax = df.plot.area(linewidth=0, colormap="Pastel1")

The trick is using the 'color' parameter:
Soln 1: dict
Simply pass a dict of {column name: color}
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'], )
ax = df.plot.area(color={'b':'0', 'c':'#17A589', 'a':'#9C640C', 'd':'#ECF0F1'})
Soln 2: sequence
Simply pass a sequence of color codes (it will match the order of your columns).
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'], )
ax = df.plot.area(color=('0', '#17A589', '#9C640C', '#ECF0F1'))
No need to set linewidth (it will automatically adjust colors). Also, this wouldn't mess with the legend.

The API of matplotlib is really complex, but here artist Module gives a very plain illustration. For the bar/barh plots, the attributes can be visited and modified by .patches, but for the area plot they need to be with .collections.
To achieve the specific modification, use codes like this.
import pandas as pd
df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
ax = df.plot.area(linewidth=0);
for collection in ax.collections:
collection.set_facecolor('#888888')
highlight = 0
ax.collections[highlight].set_facecolor('#aa3333')
Other methods of the collections can be found by run
dir(ax.collections[highlight])

Related

FacetGrid plot with aggregate in Seaborn/other library

I've toy-dataframe like this:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
df = pd.DataFrame({'cat': ['a', 'a', 'a', 'b', 'b', 'b'], 'n1': [1,1,1,4,5,6], 'n2': [6,5,2,2,2,1]})
I want to groupby by cat and plot histograms for n1 and n2, additionally I want to plot those histograms without grouping, so first, transform data to seaborn format:
df2 = pd.melt(df, id_vars='cat', value_vars=['n1', 'n2'], value_name='value')
second add "all":
df_all = df2.copy()
df_all['cat'] = 'all'
df3 = pd.concat([df2, df_all])
Finally plot:
g = sns.FacetGrid(df2, col="variable", row="cat")
g.map(plt.hist, 'value', ec="k")
I wonder, if it could be done in more elegant, concise way, without creating df3 or df2. Different library could be used.
As I mentioned in my comment, I think what you do is perfectly fine. Craft a function if needed to perform often. Nevertheless, you might be interested in pandas_profiling. This describes in detail the profile of your data, and in an interactive way. In my opinion, this is probably overkill for what you want to do, but I'll let you be the judge of that ;)
import pandas_profiling
df.profile_report()
Extract of the interactive output:

How can I print it out in this order: table, bar chart, table ...?

How can I print it out in this order: table, bar chart, table, bar chart, ...?
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.rand(100, 10),
columns=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'])
for column in df:
print(df[column].value_counts(normalize=True, bins=10))
print(df[column].hist(bins=10))
It prints all tables first. Then prints one joint bar chart. But I want to mix tables and bar charts.
What do you mean by tables? Are you doing plt.show() to get your plots?
for column in df:
print(df[column].value_counts(normalize=True, bins=10))
print(df[column].hist(bins=10))
plt.show()
Shows me the value value_counts with each individual plot. If you do it outside of the loop, the plots would just accumulate it unless you clear them.

How to use double markers in matplotlib

I wish to indicate that one curve is a hybrid of two other curves. I thought it would be a good idea to use the upper/lower triangles, and an overlay/superposition of the two for the hybrid. I.e.
'^'
'v'
a hexagram
Problem: there's no hexagram marker. And I don't manage to use any of the latex symbols (\DavidStar, \davidsstar, \largestarofdavid). How can I do so?
Alternative strategy:
overlay '^' and 'v'. How?
Alternative strategy: use some other 3 symbols that complement one another. However, I cannot find such a set in matplotlib.
Edit
In response to comments I tried this:
from matplotlib import pyplot
import numpy as np
fig, ax = pyplot.subplots()
a = np.arange(5)
lh1, = ax.plot(a, a, 'k', marker='v',ms=30,markerfacecolor='none',markeredgewidth=1.5)
lh2, = ax.plot(a, a, 'k', marker='^',ms=30,markerfacecolor='none',markeredgewidth=1.5)
ax.legend([lh1,lh2], ['1','2'] )
And this:
lh1, = ax.plot(a, a , 'r', marker=10 ,ms=30,markerfacecolor='none',markeredgewidth=1.5)
lh2, = ax.plot(a, 2*a, 'y', marker=11 ,ms=30,markerfacecolor='none',markeredgewidth=1.5)
lh3, = ax.plot(a, 3*a, 'o', marker='D',ms=30,markerfacecolor='none',markeredgewidth=1.5)
But the result is not good, and I'd like to avoid this type of hacking.

bars not proportional to value - matplotlib bar chart [duplicate]

This question already has an answer here:
Difference in plotting with different matplotlib versions
(1 answer)
Closed 4 years ago.
I am new to matplotlib and am trying to plot a bar chart using pyplot. Instead of getting a plot where the height of bar represents the value, I am getting bars that are linearly increasing in height while their values are displayed on the y-axis as labels.
payment_modes = ['Q', 'NO', 'A', 'C', 'P', 'E', 'D']
l1=[]
l2=[]
for i in payment_modes:
l.append(str(len(df[df['PMODE_FEB18']==i])))
# here l = ['33906', '37997', '815', '4350', '893', '98', '6']
plt.figure()
plt.bar(range(7),l)
This is what I am getting:
The problem is that you seem to be feeding bar with strings, not with numerical quantities. If you instead use the actual numerical quantities, bar will behave as you would expect:
import matplotlib.pyplot as plt
l = [33906, 37997, 815, 4350, 893, 98, 6]
plt.figure()
plt.bar(range(7),l)
plt.show()
gives

Apply Series function to the whole dataframe

Well, I knew that function on each" cell" can applies to the whole dataframe using applymap()
However, is there any way to apply Series function,eg: str.upper() to the whole dataframe
Yes, it could be applied directly to the applymap method of the dataframe.
Demo:
df = pd.DataFrame([['a', 'b'], ['c', 'd'], ['e', 'f']])
df
Various possiblities:
1) applymap dataframe:
df.applymap(str.upper)
2) stack + unstack combo:
df.stack().str.upper().unstack()
3) apply series:
df.apply(lambda x: x.str.upper())
All produce: