2011-04-13 8 views
55

मैं पाइपप्लॉट का उपयोग करके एक प्लॉट बनाने की कोशिश कर रहा हूं जिसमें एक असंतुलित एक्स-अक्ष है। हमेशा की तरह इस तैयार की है कि धुरी कुछ इस तरह होगा:पायथन/मैटलप्लिब - क्या एक असंतुलित धुरी बनाने का कोई तरीका है?

(मान) ---- // ---- (बाद में मान)

जहां // इंगित करता है कि आप कर रहे हैं (मान) और (बाद के मान) के बीच सब कुछ छोड़ना।

मैं इस के किसी भी उदाहरण को खोजने में सक्षम नहीं हूं, इसलिए मुझे आश्चर्य है कि यह भी संभव है या नहीं। मुझे पता है कि आप वित्तीय डेटा के लिए असंतोष पर डेटा में शामिल हो सकते हैं, लेकिन मैं धुरी में कूद को और अधिक स्पष्ट बनाना चाहता हूं। फिलहाल मैं बस सबप्लॉट्स का उपयोग कर रहा हूं लेकिन मैं वास्तव में अंत में एक ही ग्राफ पर सबकुछ खत्म करना चाहता हूं।

उत्तर

56

पॉल जवाब ऐसा करने का एक बिल्कुल ठीक तरीका है।

हालांकि, यदि आप कस्टम ट्रांसफॉर्म नहीं करना चाहते हैं, तो आप एक ही प्रभाव बनाने के लिए केवल दो सबप्लॉट का उपयोग कर सकते हैं।

स्क्रैच से एक उदाहरण को रखने के बजाय, matplotlib उदाहरणों में an excellent example of this written by Paul Ivanov है (यह केवल वर्तमान गिट टिप में है, क्योंकि यह केवल कुछ महीने पहले ही किया गया था। यह अभी तक वेबपृष्ठ पर नहीं है।)।

यह वाई-अक्ष के बजाय एक असंतुलित एक्स-अक्ष होने के लिए इस उदाहरण का एक साधारण संशोधन है। (इसीलिए मैं इस पोस्ट एक सीडब्ल्यू बना रही हूँ)

असल में, तुम सिर्फ कुछ इस तरह करते हैं:

import matplotlib.pylab as plt 
import numpy as np 

# If you're not familiar with np.r_, don't worry too much about this. It's just 
# a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing. 
x = np.r_[0:1:0.1, 9:10:0.1] 
y = np.sin(x) 

fig,(ax,ax2) = plt.subplots(1, 2, sharey=True) 

# plot the same data on both axes 
ax.plot(x, y, 'bo') 
ax2.plot(x, y, 'bo') 

# zoom-in/limit the view to different portions of the data 
ax.set_xlim(0,1) # most of the data 
ax2.set_xlim(9,10) # outliers only 

# hide the spines between ax and ax2 
ax.spines['right'].set_visible(False) 
ax2.spines['left'].set_visible(False) 
ax.yaxis.tick_left() 
ax.tick_params(labeltop='off') # don't put tick labels at the top 
ax2.yaxis.tick_right() 

# Make the spacing between the two axes a bit smaller 
plt.subplots_adjust(wspace=0.15) 

plt.show() 

enter image description here

टूटा अक्ष लाइनों // प्रभाव जोड़ने के लिए, हम कर सकते हैं इस (फिर से, पॉल इवानोव के उदाहरण से संशोधित):

import matplotlib.pylab as plt 
import numpy as np 

# If you're not familiar with np.r_, don't worry too much about this. It's just 
# a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing. 
x = np.r_[0:1:0.1, 9:10:0.1] 
y = np.sin(x) 

fig,(ax,ax2) = plt.subplots(1, 2, sharey=True) 

# plot the same data on both axes 
ax.plot(x, y, 'bo') 
ax2.plot(x, y, 'bo') 

# zoom-in/limit the view to different portions of the data 
ax.set_xlim(0,1) # most of the data 
ax2.set_xlim(9,10) # outliers only 

# hide the spines between ax and ax2 
ax.spines['right'].set_visible(False) 
ax2.spines['left'].set_visible(False) 
ax.yaxis.tick_left() 
ax.tick_params(labeltop='off') # don't put tick labels at the top 
ax2.yaxis.tick_right() 

# Make the spacing between the two axes a bit smaller 
plt.subplots_adjust(wspace=0.15) 

# This looks pretty good, and was fairly painless, but you can get that 
# cut-out diagonal lines look with just a bit more work. The important 
# thing to know here is that in axes coordinates, which are always 
# between 0-1, spine endpoints are at these locations (0,0), (0,1), 
# (1,0), and (1,1). Thus, we just need to put the diagonals in the 
# appropriate corners of each of our axes, and so long as we use the 
# right transform and disable clipping. 

d = .015 # how big to make the diagonal lines in axes coordinates 
# arguments to pass plot, just so we don't keep repeating them 
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) 
ax.plot((1-d,1+d),(-d,+d), **kwargs) # top-left diagonal 
ax.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-left diagonal 

kwargs.update(transform=ax2.transAxes) # switch to the bottom axes 
ax2.plot((-d,d),(-d,+d), **kwargs) # top-right diagonal 
ax2.plot((-d,d),(1-d,1+d), **kwargs) # bottom-right diagonal 

# What's cool about this is that now if we vary the distance between 
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(), 
# the diagonal lines will move accordingly, and stay right at the tips 
# of the spines they are 'breaking' 

plt.show() 

enter image description here

+5

मैं इसे बेहतर नहीं कह सकता था;) –

+1

'//' प्रभाव बनाने की विधि केवल उप-आंकड़ों का अनुपात 1: 1 है, तो अच्छी तरह से काम करने लगती है। क्या आप जानते हैं कि इसे किसी भी अनुपात के साथ कैसे पेश किया जाए, उदा। 'GridSpec (width_ratio = [n, m])'? –

23

मुझे इस सुविधा के लिए कई सुझाव दिखाई देते हैं लेकिन कोई संकेत नहीं है कि इसे लागू किया गया है। समय के लिए एक व्यावहारिक समाधान यहाँ है। यह एक्स-अक्ष में एक चरण-फ़ंक्शन ट्रांसफॉर्म लागू करता है। यह बहुत सारे कोड है, लेकिन यह काफी सरल है क्योंकि इसमें से अधिकांश बॉयलरप्लेट कस्टम स्केल सामान है। मैंने ब्रेक के स्थान को इंगित करने के लिए कोई ग्राफिक्स नहीं जोड़ा है, क्योंकि यह शैली का विषय है। नौकरी खत्म करने की शुभकामनाएँ।

from matplotlib import pyplot as plt 
from matplotlib import scale as mscale 
from matplotlib import transforms as mtransforms 
import numpy as np 

def CustomScaleFactory(l, u): 
    class CustomScale(mscale.ScaleBase): 
     name = 'custom' 

     def __init__(self, axis, **kwargs): 
      mscale.ScaleBase.__init__(self) 
      self.thresh = None #thresh 

     def get_transform(self): 
      return self.CustomTransform(self.thresh) 

     def set_default_locators_and_formatters(self, axis): 
      pass 

     class CustomTransform(mtransforms.Transform): 
      input_dims = 1 
      output_dims = 1 
      is_separable = True 
      lower = l 
      upper = u 
      def __init__(self, thresh): 
       mtransforms.Transform.__init__(self) 
       self.thresh = thresh 

      def transform(self, a): 
       aa = a.copy() 
       aa[a>self.lower] = a[a>self.lower]-(self.upper-self.lower) 
       aa[(a>self.lower)&(a<self.upper)] = self.lower 
       return aa 

      def inverted(self): 
       return CustomScale.InvertedCustomTransform(self.thresh) 

     class InvertedCustomTransform(mtransforms.Transform): 
      input_dims = 1 
      output_dims = 1 
      is_separable = True 
      lower = l 
      upper = u 

      def __init__(self, thresh): 
       mtransforms.Transform.__init__(self) 
       self.thresh = thresh 

      def transform(self, a): 
       aa = a.copy() 
       aa[a>self.lower] = a[a>self.lower]+(self.upper-self.lower) 
       return aa 

      def inverted(self): 
       return CustomScale.CustomTransform(self.thresh) 

    return CustomScale 

mscale.register_scale(CustomScaleFactory(1.12, 8.88)) 

x = np.concatenate((np.linspace(0,1,10), np.linspace(9,10,10))) 
xticks = np.concatenate((np.linspace(0,1,6), np.linspace(9,10,6))) 
y = np.sin(x) 
plt.plot(x, y, '.') 
ax = plt.gca() 
ax.set_xscale('custom') 
ax.set_xticks(xticks) 
plt.show() 

enter image description here

+0

मुझे लगता है कि अब के लिए क्या करना है बस जाएगा। यह मेरी पहली बार कस्टम अक्षों के साथ गड़बड़ हो जाएगा, इसलिए हमें यह देखना होगा कि यह कैसा चल रहा है। –

+0

'उलटा हुआ कस्टम ट्रांसफॉर्म' के 'डीफ़ ट्रांसफॉर्म' में एक छोटा टाइपो है, जहां इसे 'ऊपरी' के बजाय 'self.upper' पढ़ना चाहिए। महान उदाहरण के लिए धन्यवाद, यद्यपि! –

+0

क्या आप अपनी कक्षा का उपयोग करने के तरीके को दिखाने के लिए कुछ पंक्तियां जोड़ सकते हैं? –

0

फ्रेडरिक नॉर्ड के सवाल को संबोधित करते हुए अनुपात को असमान 1: 1 के साथ ग्रिडस्पेक का उपयोग करते समय विकर्ण "ब्रेकिंग" लाइनों के समानांतर अभिविन्यास को सक्षम करने के लिए, पॉल इवानोव और जो किंगटन के प्रस्तावों के आधार पर निम्नलिखित परिवर्तन सहायक हो सकते हैं। वैरिएबल एन और एम का उपयोग करके चौड़ाई अनुपात भिन्न किया जा सकता है।

import matplotlib.pylab as plt 
import numpy as np 
import matplotlib.gridspec as gridspec 

x = np.r_[0:1:0.1, 9:10:0.1] 
y = np.sin(x) 

n = 5; m = 1; 
gs = gridspec.GridSpec(1,2, width_ratios = [n,m]) 

plt.figure(figsize=(10,8)) 

ax = plt.subplot(gs[0,0]) 
ax2 = plt.subplot(gs[0,1], sharey = ax) 
plt.setp(ax2.get_yticklabels(), visible=False) 
plt.subplots_adjust(wspace = 0.1) 

ax.plot(x, y, 'bo') 
ax2.plot(x, y, 'bo') 

ax.set_xlim(0,1) 
ax2.set_xlim(10,8) 

# hide the spines between ax and ax2 
ax.spines['right'].set_visible(False) 
ax2.spines['left'].set_visible(False) 
ax.yaxis.tick_left() 
ax.tick_params(labeltop='off') # don't put tick labels at the top 
ax2.yaxis.tick_right() 

d = .015 # how big to make the diagonal lines in axes coordinates 
# arguments to pass plot, just so we don't keep repeating them 
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) 

on = (n+m)/n; om = (n+m)/m; 
ax.plot((1-d*on,1+d*on),(-d,d), **kwargs) # bottom-left diagonal 
ax.plot((1-d*on,1+d*on),(1-d,1+d), **kwargs) # top-left diagonal 
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes 
ax2.plot((-d*om,d*om),(-d,d), **kwargs) # bottom-right diagonal 
ax2.plot((-d*om,d*om),(1-d,1+d), **kwargs) # top-right diagonal 

plt.show() 
6

चेक brokenaxes पैकेज:

import matplotlib.pyplot as plt 
from brokenaxes import brokenaxes 
import numpy as np 

fig = plt.figure(figsize=(5,2)) 
bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) 
x = np.linspace(0, 1, 100) 
bax.plot(x, np.sin(10 * x), label='sin') 
bax.plot(x, np.cos(10 * x), label='cos') 
bax.legend(loc=3) 
bax.set_xlabel('time') 
bax.set_ylabel('value') 

example from brokenaxes

+0

स्थापित किए जाने के बाद पिचर्म समुदाय 2016.3.2 में टूटाएक्स आयात टूटाएक्स '' से नहीं हो सकता है। @ ben.dichter –

+0

एक बग था। मैंने ठीक कर दिया। कृपया कोड के निश्चित संस्करण को स्थापित करने के लिए 'pip इंस्टॉल टूटाक्स == 0.2' चलाएं। –

+0

ax.grid (True) के साथ बुरी तरह से बातचीत करने लगता है – innisfree

संबंधित मुद्दे