Search code examples
pythonrmatplotlibscatter-plotjitter

Matplotlib: Non-random jitter


I have read the question Matplotlib: avoiding overlapping datapoints in a "scatter/dot/beeswarm" plot and the question Adding a scatter of points to a boxplot using matplotlib however I would like to generate plots like these generated with R:

R plots
(source: eklund at www.cbs.dtu.dk)

Here is the code used for those figures.

I would like to do it with matplotlib but so far I have only managed to use np.random.normal(i, 0.05). The dots separate from each other, but I'd like to have them ordered.

This answer does something similar to what I want but my data are float numbers which are very close but are different, therefore the groupby function does not work, and I'd like to have the dots symmetric to the center as shown in the plots generated with R shown above.


Solution

  • As pointed in the Edit of this question Matplotlib: avoiding overlapping datapoints in a "scatter/dot/beeswarm" plot and I did not read at the beginning, there is a python package for that kind of plots:

    https://github.com/mgymrek/pybeeswarm

    And definitely that package does a much better job than the code below.

    I modified the code of this answer to accept float numbers and I got something slightly similar to what I wanted. Here is the code:

    CA = [0,4,0,3,0,5]  
    CB = [0,0,4,4,2,2,2,2,3,0,5] 
    CC = [0.08423, 4.0078, 0.02936, 0.04862, 3.2105, 3.7796, 1.9974, 1.6986, 1.7443, 1.6615, 1, 1, 1]
    
    lists = [CA, CB, CC]
    
    x = []
    y = []
    for index1, my_list in enumerate(lists):
        scores_bins = {}
        for index2, score in enumerate(my_list):
            binx = round(score, 1)
            if binx not in scores_bins:
                scores_bins[binx] = []
            scores_bins[binx].append(score)
    
        for key, val in sorted(scores_bins.items()):
            values = scores_bins[key]
            points = len(values)
            pos = 1 + index1 + (1 - points) / 50.
            for value in values:
                x.append(pos)
                y.append(value)
                pos += 0.05
    
    plt.plot(x, y, 'o')
    plt.xlim((0,4))
    plt.ylim((-1,6))
    
    plt.show()
    

    However, if pos is increased the dots move more to the right instead of just spreading from the center to the right and left...