2013-12-17 73 views
3

此鏈接R/GGPLOT2顏色爲R代碼複製ggplot的顏色:Plotting family of functions with qplot without duplicating data複製在python

我在複製在Python代碼有一展身手 - 但結果不正確......

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
import math 
import colorsys 

# function to return a list of hex colour strings 
def colorMaker(n=12, start=15.0/360.0, saturation=1.0, valight=0.65) : 
    listOfColours = [] 
    for i in range(n) : 
     hue = math.modf(float(i)/float(n) + start)[0] 
     #(r,g,b) = colorsys.hsv_to_rgb(hue, saturation, valight) 
     (r,g,b) = colorsys.hls_to_rgb(hue, valight, saturation) 
     listOfColours.append('#%02x%02x%02x' % (int(r*255), int(g*255), int(b*255))) 
    return listOfColours 

# made up data 
x = np.array(range(20)) 
d = {} 
d['y1'] = pd.Series(x, index=x) 
d['y2'] = pd.Series(1.5*x + 1, index=x) 
d['y3'] = pd.Series(2*x + 2, index=x) 
df = pd.DataFrame(d) 

# plot example 
plt.figure(num=1, figsize=(10,5), dpi=100) # set default image size 
colours = colorMaker(n=3) 
df.plot(linewidth=2.0, color=colours) 
fig = plt.gcf() 
fig.savefig('test.png') 

結果...

enter image description here

回答

1

仔細觀察gg代碼 - 它看起來像我有兩個問題。首先是分母應該是n + 1,而不是n。第二個問題是我需要色度 - 色度 - 亮度轉換工具;而不是我一直採用的色相 - 飽和度飽和度方法。不幸的是,我並不知道python中的hcl_to_rgb工具;所以寫了一個。

我的解決方案(如下)解決了這兩個問題。在我看來,我認爲它複製了ggplot2的顏色。

import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
import math 
import collections 

def hcl_to_rgb(hue=0, chroma=0, luma=0) : 
    # Notes: 
    # coded from http://en.wikipedia.org/wiki/HSL_and_HSV#From_luma.2Fchroma.2Fhue 
    # with insights from gem.c in MagickCore 6.7.8 
    # http://www.imagemagick.org/api/MagickCore/gem_8c_source.html 
    # Assume: 
    # h, c, l all in range 0 .. 1 (cylindrical coordinates) 
    # Returns a tuple: 
    # r, g, b all in the range 0 .. 1 (cubic cartesian coordinates) 

    # sanity checks 
    hue = math.modf(float(hue))[0] 
    if hue < 0 or hue >= 1 : 
     raise ValueError('hue is a value greater than or equal to 0 and less than 1') 
    chroma = float(chroma) 
    if chroma < 0 or chroma > 1 : 
     raise ValueError('chroma is a value between 0 and 1') 
    luma = float(luma) 
    if luma < 0 or luma > 1 : 
     raise ValueError('luma is a value between 0 and 1') 

    # do the conversion 
    _h = hue * 6.0 
    x = chroma * (1 - abs((_h % 2) - 1)) 

    c = chroma 
    if 0 <= _h and _h < 1 : 
     r, g, b = (c, x, 0.0) 
    elif 1 <= _h and _h < 2 : 
     r, g, b = (x, c, 0.0) 
    elif 2 <= _h and _h < 3 : 
     r, g, b = (0.0, c, x) 
    elif 3 <= _h and _h < 4 : 
     r, g, b = (0.0, x, c) 
    elif 4 <= _h and _h < 5 : 
     r, g, b = (x, 0.0, c) 
    elif 5 <= _h and _h <= 6 : 
     r, g, b = (c, 0.0, x) 
    else : 
     r, g, b = (0.0, 0.0, 0.0) 

    m = luma - (0.298839*r + 0.586811*g + 0.114350*b) 
    z = 1.0 
    if m < 0.0 : 
     z = luma/(luma-m) 
     m = 0.0 
    elif m + c > 1.0 : 
     z = (1.0-luma)/(m+c-luma) 
     m = 1.0 - z * c 
    (r, g, b) = (z*r+m, z*g+m, z*b+m) 

    # clipping ... 
    (r, g, b) = (min(r, 1.0), min(g, 1.0), min(b, 1.0)) 
    (r, g, b) = (max(r, 0.0), max(g, 0.0), max(b, 0.0)) 
    return (r, g, b) 

def ggColorSlice(n=12, hue=(0.004,1.00399), chroma=0.8, luma=0.6, skipHue=True) : 
    # Assume: 
    # n: integer >= 1 
    # hue[from, to]: all floats - red = 0; green = 0.33333 (or -0.66667) ; blue = 0.66667 (or -0.33333) 
    # chroma[from, to]: floats all in range 0 .. 1 
    # luma[from, to]: floats all in range 0 .. 1 
    # Returns a list of #rgb colour strings: 

    # convert stand alone values to ranges 
    if not isinstance(hue, collections.Iterable): 
     hue = (hue, hue) 
    if not isinstance(chroma, collections.Iterable): 
     chroma = (chroma, chroma) 
    if not isinstance(luma, collections.Iterable): 
     luma = (luma, luma) 

    # convert ints to floats 
    hue = [float(hue[y]) for y in (0, 1)] 
    chroma = [float(chroma[y]) for y in (0, 1)] 
    luma = [float(luma[y]) for y in (0, 1)] 

    # some sanity checks 
    n = int(n) 
    if n < 1 or n > 360 : 
     raise ValueError('n is a value between 1 and 360') 
    if any([chroma[y] < 0.0 or chroma[y] > 1.0 for y in (0, 1)]) : 
     raise ValueError('chroma is a value between 0 and 1') 
    if any([luma[y] < 0.0 or luma[y] > 1.0 for y in (0, 1)]) : 
     raise ValueError('luma is a value between 0 and 1') 

    # generate a list of hex colour strings 
    x = n + 1 if n % 2 else n 
    if n > 1 : 
     lDiff = (luma[1] - luma[0])/float(n - 1.0) 
     cDiff = (chroma[1] - chroma[0])/float(n - 1.0) 
     if skipHue : 
      hDiff = (hue[1] - hue[0])/float(x) 
     else : 
      hDiff = (hue[1] - hue[0])/float(x - 1.0) 
    else: 
     hDiff = 0.0 
     lDiff = 0.0 
     cDiff = 0.0 

    listOfColours = [] 
    for i in range(n) : 
     c = chroma[0] + i * cDiff 
     l = luma[0] + i * lDiff 
     h = math.modf(hue[0] + i * hDiff)[0] 
     h = h + 1 if h < 0.0 else h 
     (h, c, l) = (min(h, 0.99999999999), min(c, 1.0), min(l, 1.0)) 
     (h, c, l) = (max(h, 0.0), max(c, 0.0), max(l, 0.0)) 
     (r, g, b) = hcl_to_rgb(h, c, l) 
     listOfColours.append('#%02x%02x%02x' % (int(r*255), int(g*255), int(b*255))) 
    return listOfColours 

for i in range(1, 20) : 
    # made up data 
    x = np.array(range(20)) 
    d = {} 
    for j in range(1, i+1) : 
     y = x * (1.0 + j/10.0) + j/10.0 
     d['y'+'%03.0d' % j] = pd.Series(data=y , index=x) 
    df = pd.DataFrame(d) 

    # plot example 
    plt.figure(num=1, figsize=(10,5), dpi=100) # set default image size 
    colours = ggColorSlice(n=i) 
    if len(colours) == 1: 
     colours = colours[0] # kludge 
    df.plot(linewidth=4.0, color=colours) 
    fig = plt.gcf() 
    f = 'test-'+str(i)+'.png' 
    print f 
    plt.title(f) 
    fig.savefig(f) 
    plt.close() 

enter image description here

enter image description here

enter image description here

8

您應該檢查mpltools一個非常整潔的matplotlib擴展。有一個ggplot風格見here。 例如(來自鏈接): enter image description here

+4

+1。我也想建議Seaborn:https://github.com/mwaskom/seaborn; Python-ggplot:https://github.com/yhat/ggplot;和prettyplotlib:https://github.com/olgabot/prettyplotlib –

+0

@PaulH我認爲這是值得的自己的答案 – tacaswell

+0

@PaulH是啊請將此作爲一個新的答案,這些都是偉大的包! – Jakob

4

已經有大約統一努力構建以更直接的方式創建美觀的地塊上matplotlib的頂庫的talk lately了相當數量。目前,有幾個選項。不幸的是,這個生態系統非常分散。

除了由雅各布提到mpltools,我想指出,這三個:

如果你熟悉的r已經GGPLOT2,你應該感到與Python-ggplot很舒服,我衷心地鼓勵你們做出貢獻。就我個人而言,我認爲我永遠不會想到這種繪圖API的風格(這是對我自己的批評,而不是ggplot)。最後,我粗略地看Seaborn和prettyplotlib讓我相信它們更像mpltools,因爲它們提供了基於matplotlib的便利功能。

從事物的聲音來看,大熊貓社區至少在增加海豹和ggplot的努力。我個人對此感到興奮。值得一提的是,所有的努力都是建立在matplotlib之上 - 而不是替代 - 。我認爲大多數人(包括自己)非常感謝MPL開發者在過去十年中創建的強大而通用的框架。

1

你要沿着HUSL色彩空間的色調均勻尺寸樣品,其中有一個很好的輕量級Python package。這是seaborn中使用的。