Search code examples
pythonchemistry

MALDI graph: need help labeling the local maxima


This following code works fine, but I am not able to label on the peak of the signficant columns. I am trying to label the x value of the tallest columsns in each cluster of columns. The peaks that I would like labeled here are the ones at: 630, 637, and 690 m/z. The code is below.

Here is the image of the graph.

import matplotlib.pyplot as plt
import numpy as np

x = []
y = []
for line in open('Maldi', 'r'):
    lines = [i for i in line.split()]
    x.append(float(lines[0]))
    y.append(float(lines[1]))

 plt.title("Spectra")
 plt.xlabel('m/z')
 plt.ylabel('Intensity')
 plt.bar(x, y, width=0.05)
 plt.xlim([500, 1000])
 plt.show()

Solution

  • As mentioned by @bfris, you can find documentation on styling and positioning annotation on the [Matplotlib website]. They have specific annotation demonstrations, an annotation tutorial, or you can look at the user guide.
    You will need to instantiate the axes object to add annotations by including the statement:

    fig, ax = plt.subplots()
    

    I think the easiest way to position text intuitively may be to specify the 'axes fraction' where (0, 0) is the lower left of the axes and (1, 1) is the upper right corner of the axes. This would make finding the x position convenient since you know the m/z value for the peaks you want to label. Values of 630, 637, and 690 would result in precise axes fraction x-coordinate values of 0.26, 0.274, and 0.38, respectively. You can find y coordinates the same way if you know the precise values of the peaks, or you could just assign arbitrary values, view the result, and adjust accordingly. For the sake of demonstrating what the code may look like, I am going to assume y-values of 4500, 2950, and 1875 as well as axes fraction y-values of 0.6, 0.4, and 0.3 for the three peaks. Note that even if I had precise y-coordinate values, the annotation would need to be offset either above or to the left or right, so these values will need to be adjusted to find the most aesthetically pleasing position.

    import matplotlib.pyplot as plt
    import numpy as np
    
    fig, ax = plt.subplots()
    
    x = []
    y = []
    for line in open('Maldi', 'r'):
        lines = [i for i in line.split()]
        x.append(float(lines[0]))
        y.append(float(lines[1]))
    
    ax.set_title("Spectra")
    ax.set_xlabel('m/z')
    ax.set_ylabel('Intensity')
    ax.bar(x, y, width=0.05)
    ax.set_xlim([500, 1000])
    
    ax.annotate('630', (630, 4500), xytext=(0.26, 0.6), textcoords='axes fraction')
    ax.annotate('637', (637, 2950), xytext=(0.274, 0.4), textcoords='axes fraction')
    ax.annotate('690', (690, 1875), xytext=(0.38, 0.3), textcoords='axes fraction')
    
    plt.show()
    

    Without your Maldi file, I cannot recreate the plot, so my apologies if there are bugs in that code that I didn't catch. It's also worth noting that if you specify 'axes fraction' to position the annotations, the position is relative to the size of the axes. If you adjust the size of the axes, it may effect the spacing of the annotations relative to the data.
    Good luck!