How to speed up matplotlib scatter ploting? - performance

I need to draw a batch of scatter charts in matplotlib, and found the speed of matplotlib is slow, then I lineprofile the function, and found the hotspot is fig, ax = plt.subplots(), It costs 56.1% of time to creat a blank figure and axes !!
How to speed it up ? I mean, how can I reuse fig and ax to avoid creating them each time ?
Attach the profile report here (I cut some of the line to make it simple)
Total time: 0.733771 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
517 #profile
518 def df_scatter(df):
519 ''' draw the scatter plot for Pandas dataframe 'df'
533 '''
536
537 1 75 75.0 0.0 biggest_area = 1000
538 1 117 117.0 0.0 mycm = matplotlib.cm.get_cmap('jet') # 'spectral'
539
541 1 78 78.0 0.0 col_qty = len(df.columns)
543
544 1 1859 1859.0 0.1 x = list(df.ix[:,0].values)
545 1 1258 1258.0 0.0 y = list(df.ix[:,1].values)
551
552 1 1472345 1472345.0 56.1 fig, ax = plt.subplots()
556
557 1 7007 7007.0 0.3 plt.subplots_adjust(left=0.07, right=0.92, bottom=0.1, top=0.95)
558 1 179 179.0 0.0 x_margin, y_margin = (max(x)-min(x))/20, (max(y)-min(y))/20
563
564 1 71 71.0 0.0 if col_qty > 2:
565 1 1602 1602.0 0.1 r = list(df.ix[:,2].values)
566 1 309 309.0 0.0 size_r = np.array(biggest_area)*r/max(r)
585
586 1 34712 34712.0 1.3 sc = plt.scatter(x, y, marker='o', s=size_r, cmap=mycm, alpha=0.65)
587
588 # adding colorbar
589 1 542417 542417.0 20.7 cbaxes = fig.add_axes([0.94, 0.25, 0.02, 0.70])
590 1 165719 165719.0 6.3 cbar = plt.colorbar(sc, cax=cbaxes)
591 1 122 122.0 0.0 cbar.solids.set_edgecolor('face')
595
602 1 1061 1061.0 0.0 plt.figtext(0.94,0.10,"%0.1f"%(max(r)), fontproperties=TEXT_FONT_MEDIUM)
639 1 66 66.0 0.0 return fig

I think that the best way to do it is calling
fig = plt.figure()
ax=fig.add_subplot(111)
from outside of df_scatter. Then, pass it to df_scatter as arguments:
df_scatter(df,fig,ax):
or simply do inside df_scatter:
def df_scatter(df):
fig = plt.gcf()
ax = plt.gca()
after the creation of fig & axis was done.

Related

Processing 2d scatter plot

How to load .csv file and use data in the table to create 2d scatter plot.
The data inside the table is
Name X Y Group Gender Year of Birth Grade
Victor Anderson 627 705 2 Female 2000 6
Jack Scott 808 643 1 Male 2002 4
Sean Robinson 624 627 1 Male 2002 4
William Rodriguez 423 396 1 Female 2004 2
Aaron Kelly 775 181 0 Female 2005 1
Raymond Taylor 433 731 1 Female 2000 1
Alan Foster 635 580 2 Male 2002 4
Charles Watson 884 262 0 Female 2003 3
Lillian Perez 334 190 4 Male 2005 1
Betty Moore 727 524 1 Female 2003 3
Bruce Adams 503 684 0 Male 2001 2
Kathryn Sanchez 284 246 0 Male 2001 2
Chris Hall 189 223 3 Male 2000 6
Eugene Harris 196 220 3 Female 2000 5
Gary Baker 707 559 0 Female 2001 5
Michael Ramirez 299 657 2 Male 2000 1
Walter Smith 400 755 0 Male 2005 5
Ann Murphy 0 895 1 Male 2003 3
Lois Thompson 783 631 1 Female 2003 2
Louis Jones 674 589 1 Female 2003 3
Tammy Bell 828 263 3 Male 2002 4
Carolyn Bailey 557 23 4 Male 2004 2
Larry Campbell 444 627 2 Female 2004 2
To draw a simple scatter plot from the data in the csv we need to:
Ensure X and Y columns can be read as numbers
Normalize or scale the X and Y values to the canvas size
Plot a point for each row at its scaled x and y position
Add labels
For the first item we could add code to clean up X and Y and turn them into numbers. Below I have assumed that X and Y will always be integers and removed white space so we can just call row.getInt()
The data is saved like this:
Name,X,Y,Group,Gender,Year of Birth,Grade
Victor Anderson,627,705,2,Female,2000,6
Jack Scott,808,643,1,Male,2002,4
Notice that extra white space has been removed so that we can call table and row methods without special cleanup code.
Table table;
int xMin;
int yMin;
int xMax;
int yMax;
void setup() {
size(900, 900);
table = loadTable("plot_data.csv", "header");
background(0);
fill(255);
// we get the minimum and maximum values for X and Y so we can scale to our canvas
xMin = getMin(table, "X");
yMin = getMin(table, "Y");
xMax = getMax(table, "X");
yMax = getMax(table, "Y");
}
void draw(){
background(0);
boolean labelDrawn = false;
for (TableRow row : table.rows()) {
int x = row.getInt("X");
// the map function takes our value and maps it to a value that fits on our canvas
float xPos = map(x, xMin, xMax, 0, width);
int y= row.getInt("Y");
float yPos = map(y, yMin, yMax, 0, height);
ellipse(xPos,yPos,10,10);
// Here we put a name label on the plotted data when the mouse is over it
if(dist(mouseX,mouseY,xPos,yPos)<10 && !labelDrawn){
fill(255);
text(row.getString("Name"),mouseX-10,mouseY-10);
labelDrawn = true; // only draw one label at a time
}
}
}
int getMin(Table table, String col){
int min = Integer.MAX_VALUE;
for (TableRow row : table.rows()){
if (row.getInt(col)<min){
min = row.getInt(col);
}
}
return min;
}
int getMax(Table table, String col){
int max = Integer.MIN_VALUE;
for (TableRow row : table.rows()){
if (row.getInt(col)>max){
max = row.getInt(col);
}
}
return max;
}

How to down sample image array, without changing pixel values

I have image segmentation project, and ground truth labels given as images where pixel value stands for the label. I need to resize the images and labels, while keeping the labels in the same value set.
I tried many things, All change the value set.
Lets create dummy data
from skimage.transform import rescale, resize
from scipy import ndimage
from PIL import Image
import cv2
mask = np.zeros((30,20), dtype=np.uint16)
mask[22:26,12:30]=70
mask[25:27,14:17]=30
print('original label', mask.shape, np.unique(mask))
Outputs: original label shape: (30, 20) original label values: [ 0 30 70]
I need to resize label, so the result will have only 0, 30, 70 values.
What I tried
skimage_resized = resize(mask, (mask.shape[0]//2, mask.shape[1]//2), mode='constant')
print(skimage_resized.shape, np.unique(mask_resized))
skimage_rescale = rescale(mask, 1.0/2.0, mode='constant')
print(skimage_rescale.shape, np.unique(mask_resized))
ndimage_resized = ndimage.interpolation.zoom(mask, 0.5)
print(ndimage_resized.shape, np.unique(mask_resized))
cv2_resized = cv2.resize(mask, (mask.shape[0]//2, mask.shape[1]//2),
interpolation=cv2.INTER_NEAREST)
print(cv2_resized.shape, np.unique(mask_resized))
mask_pil = Image.fromarray(mask, mode=None)
pil_resized = mask_pil.thumbnail((mask.shape[0]//2, mask.shape[1]//2), Image.NEAREST)
print(skimage_resized.shape, np.unique(pil_resized))
Output:
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(10, 15) [ 0 5 6 28 29 30 31 61 62 65 70 71 74 75 76]
(15, 10) [None]
Found a solution with openCV.
import numpy as np
import cv2
resizeto = 2
small_lable = cv2.resize(mask, (mask.shape[1]//resizeto,
mask.shape[0]//resizeto),
interpolation=cv2.INTER_NEAREST)
small_lable = (np.array(small_lable)).astype('uint8')
print(small_lable.shape, np.unique(small_lable))
plt.imshow(small_lable)
output:
(15, 10) [ 0 30 70]
From the docs (emphasis mine):
Note that when down-sampling an image, resize and rescale should perform Gaussian smoothing to avoid aliasing artifacts. See the anti_aliasing and anti_aliasing_sigma arguments to these functions.
Downscale serves the purpose of down-sampling an n-dimensional image by integer factors using the local mean on the elements of each block of the size factors given as a parameter to the function.
One possible workaround to your problem would be downsampling through basic slicing:
import numpy as np
dx, dy = 2, 2
mask = np.zeros((30, 20), dtype=np.uint16)
mask[22:26, 12:30] = 70
mask[25:27, 14:17] = 30
downsampled = mask[::dx, ::dy]
print(downsampled.shape, np.unique(downsampled))
The output from the snippet above is:
(15, 10) [ 0 30 70]

imcrop into 1 variable

I have made a a group of new pictures using imcrop from the same file, With this code, I know it's long but since the distances are not always the same I find no other way to do it than this:
A001=imcrop(A,[65 159 95 332]);
A002=imcrop(A,[182 161 95 332]);
A003=imcrop(A,[297 164 95 332]);
A004=imcrop(A,[402 165 90 332]);
A005=imcrop(A,[495 168 90 332]);
A006=imcrop(A,[606 166 90 332]);
A007=imcrop(A,[705 171 90 332]);
A008=imcrop(A,[808 175 90 332]);
A009=imcrop(A,[922 175 90 332]);
A0010=imcrop(A,[1031 175 90 332]);
Then I have a series of tasks to be performed on each of the new images, how do i get around that the easiest way? When I import multiple jpegs from a folder I can get it to make a dataset of the files but when I try to do the same with A001:A0010 I get nothing.
This is the task that I want to perform:
greenChannel = A(:, :, 2);
BW = edge(greenChannel,'Prewitt');
figure, imshow(BW)
%Dialate Lines
se90 = strel('line', 3, 90);
se0 = strel('line', 3, 0);
BWsdil = imdilate(BW, [se90 se0]);;
figure, imshow(BWsdil), title('dilated gradient mask');
%Fill Lines
BWdfill = imfill(BWsdil, 'holes');
figure, imshow(BWdfill);
title('binary image with filled holes');
BWnobord = imclearborder(BWdfill, 4);
figure, imshow(BWnobord), title('cleared border image');
seD = strel('diamond',1);
BWfinal = imerode(BWnobord,seD);
BWfinal = imerode(BWfinal,seD);
figure, imshow(BWfinal), title('segmented image');
L = bwlabel(BWfinal);
s = regionprops(L,'centroid');
What I need help to do is somehow get A001:A0010 into A in the top and run that sequence of commands, hope someone can help me achieve that!
This is hairy, but here goes:
A = imread('peppers.png');
A = imresize(A, [1500 1500]); % to handle the indexing range.
A001=imcrop(A,[65 159 95 332]);
A002=imcrop(A,[182 161 95 332]);
A003=imcrop(A,[297 164 95 332]);
A004=imcrop(A,[402 165 90 332]);
A005=imcrop(A,[495 168 90 332]);
A006=imcrop(A,[606 166 90 332]);
A007=imcrop(A,[705 171 90 332]);
A008=imcrop(A,[808 175 90 332]);
A009=imcrop(A,[922 175 90 332]);
A0010=imcrop(A,[1031 175 90 332]);
w = who; % returns the names of all your current variables in a cell.
for i = 1:numel(w)
% A00 is unique to all the variables you want to process.
if ~isempty(strfind(w{i}, 'A00'))
% hard coding greenChannel and extracting the second plane.
eval(['greenChannel = ',w{i},'(:,:,2)']);
% do the rest of the processing here,
% from BW = edge ... to regionprops.
% You may have to save the s structure as a cell array.
end
end
This uses the who command to extract all the current variables, and the eval command to evaluate what is passed in as text, based on the variable names. Note that using eval is dangerous, and should be done only if there are no better alternatives. See Use and implications of eval('expression') in MATLAB code?

MATLAB: finding values in one array and using those locations to change values in one channel of a separate 3 channel array

So I have a labeled array (array1) with connected regions of interest (background is all zeros, connected regions are all 1's for the first region, all 2's for the second, 3's for the 3rd etc.) I also have a vector (vector1) of the region labels I find important (ex. 1,6,9). I want to find the locations of these values in the labeled array and then change values in one channel of a separate 3 channel array at the same locations (want to color certain parts of an image green based regions of interest found in another image).
I can use the below code to change all channels, but don't know how to specify (img(y,x,1=0), img(y,x,2=0), img(y,x,3=255)).
for i=1:1:length(vector1)
img(array1==vector1(i))=255;
end
If I understand you correctly, you have a label map of objects - each with an associated ID with 0 as the background. You also have a vector of important IDs and a colour image that is associated with this label map.
You wish to set all locations that have an important ID to 1 colour. I would first create a logical mask where true means that the pixel is important and false otherwise. What I mean by important is that the pixel is either 1, 6 or 9 if we go with your example. You can use bsxfun combined with any to create this mask, then we can use this to index into your image and set the right colour to these locations.
Therefore, do this:
%// Create your logical mask
mask3D = bsxfun(#eq, array1, permute(vector(:), [3 2 1]));
mask = any(mask3D, 3);
%// Set the image pixels to blue at these locations
red = img(:,:,1);
green = img(:,:,2);
blue = img(:,:,3);
red(mask) = 0;
green(mask) = 0;
blue(mask) = 255;
img = cat(3, red, green, blue);
Here's a quick example run. Suppose we have this image with squares:
We can see that there are three squares. Let's change object 1 and object 3 to blue. Before we do that, we need to get a label map:
%// Originally a binary image
im = imread('http://i.stack.imgur.com/DnYQS.png');
%// Get label map
array1 = bwlabel(im);
%// Create colour version out of this binary image
img = 255*uint8(im);
img = cat(3, img, img, img);
array1 is our label map as you have also mentioned in your question and img is the colour version of the input image. Now, vector = [1 3] so we can change those objects. The labelling is such that the top left square is label 1, the middle is label 2 and the bottom right is label 3.
Once I do this and I run the above code, this is the image I get:
You can achieve it in two lines -
%// Create a 3D mask to select the labels [1,6,9] only, as listed in "vector1"
mask = bsxfun(#and,ismember(array1,vector1),ones(1,1,3));
%// Map "img" with "mask" and set them to tuple:[0,0,255] (blue color)
img(mask) = reshape(repmat([0 0 255],nnz(mask)/3,1),[],1)
Sample run -
array1 = %// Input
2 0 1 1 0 6
0 0 1 0 0 6
9 0 0 0 0 6
9 9 0 4 0 0
vector1 = %// Input
1 6 9
img(:,:,1) = %// Input: Before code run
228 19 175 30 192 188
204 23 34 164 149 248
188 204 185 84 189 222
14 241 29 167 60 22
img(:,:,2) =
94 202 197 59 200 136
95 94 53 164 26 24
175 53 100 124 75 104
153 23 141 39 61 27
img(:,:,3) =
29 246 111 48 197 172
201 111 168 68 101 110
75 178 28 204 70 116
154 194 239 125 10 156
img(:,:,1) = %// Output: After code run
228 19 0 0 192 0
204 23 0 164 149 0
0 204 185 84 189 0
0 0 29 167 60 22
img(:,:,2) =
94 202 0 0 200 0
95 94 0 164 26 0
0 53 100 124 75 0
0 0 141 39 61 27
img(:,:,3) =
29 246 255 255 197 255
201 111 255 68 101 255
255 178 28 204 70 255
255 255 239 125 10 156
Notice the positions of the values [1,6,9] in array1 and the corresponding value changes in img1 before and after code runs.

Preview OSX bug (?) converting bezier curve in .eps file to .pdf

I have a small .eps file which uses bezier curves with a thick linewidth to achieve a certain effect. This file displays as I expect it to in gv, but when I convert it to a .pdf (eg with ps2pdf or by opening the file with Preview on a Mac running OSX) it renders some of the curves incorrectly (or at least, not in the way I was expecting); in the middle of the curve, the normals to the curve seem to "flip over", producing a strange artifact.
Here is the .eps file in question, as code:
%!PS-Adobe-2.0 EPSF-2.0
%%BoundingBox: 0 0 750 200
gsave 50 50 scale 1 3 translate
/edge{4 dict begin
/y2 exch def
/x2 exch def
/y1 exch def
/x1 exch def
gsave
1 setgray
1 8 div setlinewidth
newpath
x1 y1 moveto x2 y2 lineto
stroke
0 setgray
1 20 div setlinewidth
newpath
x1 y1 moveto x2 y2 lineto
stroke
x1 y1 0.07 0 360 arc fill stroke
x2 y2 0.07 0 360 arc fill stroke
grestore
end}def
/cur_edge{7 dict begin
/T exch def
/angle2 exch def
/y2 exch def
/x2 exch def
/angle1 exch def
/y1 exch def
/x1 exch def
gsave
1 setgray
1 8 div setlinewidth
newpath
x1 y1 moveto
x1 angle1 cos T mul add y1 angle1 sin T mul add
x2 angle2 cos T mul add y2 angle2 sin T mul add
x2 y2 curveto
stroke
0 setgray
1 20 div setlinewidth
newpath
x1 y1 moveto
x1 angle1 cos T mul add y1 angle1 sin T mul add
x2 angle2 cos T mul add y2 angle2 sin T mul add
x2 y2 curveto
stroke
x1 y1 0.07 0 360 arc fill stroke
x2 y2 0.07 0 360 arc fill stroke
grestore
end}def
/fat_edge{7 dict begin
/T exch def
/angle2 exch def
/y2 exch def
/x2 exch def
/angle1 exch def
/y1 exch def
/x1 exch def
gsave
0 setgray
1 4 div setlinewidth
newpath
x1 y1 moveto
x1 angle1 cos T mul add y1 angle1 sin T mul add
x2 angle2 cos T mul add y2 angle2 sin T mul add
x2 y2 curveto
stroke
0.9 setgray
1 5 div setlinewidth
newpath
x1 y1 moveto
x1 angle1 cos T mul add y1 angle1 sin T mul add
x2 angle2 cos T mul add y2 angle2 sin T mul add
x2 y2 curveto
stroke
grestore
end}def
/fat_vertex{4 dict begin
/angle exch def
/y exch def
/x exch def
/T 0.14 def
gsave
0.9 setgray
1 5.5 div setlinewidth
newpath
x y moveto
x angle cos T mul add y angle sin T mul add lineto
stroke
grestore
end}def
/extra_fat_vertex{5 dict begin
/y exch def
/x exch def
/w 0.16 def
gsave
1 setgray
newpath
x w sub y w sub moveto
x w add y w sub lineto
x w add y w add lineto
x w sub y w add lineto
closepath
fill
stroke
0 setgray
/v 0.12 def
newpath
x v sub y v sub moveto
x v add y v sub lineto
x v add y v add lineto
x v sub y v add lineto
closepath
fill
0.9 setgray
/u 0.095 def
newpath
x u sub y u sub moveto
x u add y u sub lineto
x u add y u add lineto
x u sub y u add lineto
closepath
fill
grestore
end}def
/extra_fat_triangle{5 dict begin
/y exch def
/x exch def
/w 0.16 def
gsave
1 setgray
newpath
x w sub y w sub moveto
x w 1.1 mul add y lineto
x w sub y w add lineto
closepath
fill
stroke
0 setgray
/v 0.12 def
newpath
x v sub y v sub moveto
x v add y lineto
x v sub y v add lineto
closepath
fill
0.9 setgray
/u 0.095 def
newpath
x u sub y u sub moveto
x u 0.85 mul add y lineto
x u sub y u add lineto
closepath
fill
grestore
end}def
/extra_fat_triangle_left{5 dict begin
/y exch def
/x exch def
/w 0.16 def
gsave
1 setgray
newpath
x w add y w sub moveto
x w 1.1 mul sub y lineto
x w add y w add lineto
closepath
fill
stroke
0 setgray
/v 0.12 def
newpath
x v add y v sub moveto
x v sub y lineto
x v add y v add lineto
closepath
fill
0.9 setgray
/u 0.095 def
newpath
x u add y u sub moveto
x u 0.85 mul sub y lineto
x u add y u add lineto
closepath
fill
grestore
end}def
0 0 90 2 0 270 1 cur_edge
% 0 0 270 2 0 90 1 cur_edge
2 0 90 0 0 270 1 cur_edge
0 0 180 0.5 -2 180 1 cur_edge
0.5 -2 90 1.5 -1.3 180 0.5 cur_edge
1.5 -1.3 270 2.5 -2 270 0.7 cur_edge
2.5 -2 90 0.5 -2 270 1 cur_edge %
2.5 -2 0 3 -0.75 0 1 cur_edge
3 -0.75 270 1.5 -1.3 0 0.7 cur_edge
3 -0.75 180 1.5 -1.3 90 0.7 cur_edge
3 -0.75 90 2 0 0 0.5 cur_edge
5 0 translate
0 0 90 2 0 270 1 fat_edge
% 0 0 270 2 0 90 1 fat_edge
2 0 90 0 0 270 1 fat_edge
0 0 180 0.5 -2 180 1 fat_edge
0.5 -2 90 1.5 -1.3 180 0.5 fat_edge
1.5 -1.3 270 2.5 -2 270 0.7 fat_edge
2.5 -2 90 0.5 -2 270 1 fat_edge
2.5 -2 0 3 -0.75 0 1 fat_edge
3 -0.75 270 1.5 -1.3 0 0.7 fat_edge
3 -0.75 180 1.5 -1.3 90 0.7 fat_edge
3 -0.75 90 2 0 0 0.5 fat_edge
0 0 90 fat_vertex
0 0 180 fat_vertex
0 0 270 fat_vertex
2 0 0 fat_vertex
2 0 90 fat_vertex
2 0 270 fat_vertex
0.5 -2 180 fat_vertex
0.5 -2 90 fat_vertex
2.5 -2 90 fat_vertex
2.5 -2 270 fat_vertex
1.5 -1.3 0 fat_vertex
1.5 -1.3 90 fat_vertex
1.5 -1.3 180 fat_vertex
1.5 -1.3 270 fat_vertex
3 -0.75 0 fat_vertex
3 -0.75 90 fat_vertex
3 -0.75 180 fat_vertex
3 -0.75 270 fat_vertex
5 0 translate
0 0 90 2 0 270 1 fat_edge
% 0 0 270 2 0 90 1 fat_edge
2 0 90 0 0 270 1 fat_edge
0 0 180 0.5 -2 180 1 fat_edge
0.5 -2 90 1.5 -1.3 180 0.5 fat_edge
1.5 -1.3 270 2.5 -2 270 0.7 fat_edge
2.5 -2 90 0.5 -2 270 1 fat_edge
2.5 -2 0 3 -0.75 0 1 fat_edge
3 -0.75 270 1.5 -1.3 0 0.7 fat_edge
3 -0.75 180 1.5 -1.3 90 0.7 fat_edge
3 -0.75 90 2 0 0 0.5 fat_edge
0 0 90 fat_vertex
0 0 180 fat_vertex
0 0 270 fat_vertex
2 0 0 fat_vertex
2 0 90 fat_vertex
2 0 270 fat_vertex
0.5 -2 180 fat_vertex
0.5 -2 90 fat_vertex
2.5 -2 90 fat_vertex
2.5 -2 270 fat_vertex
1.5 -1.3 0 fat_vertex
1.5 -1.3 90 fat_vertex
1.5 -1.3 180 fat_vertex
1.5 -1.3 270 fat_vertex
3 -0.75 0 fat_vertex
3 -0.75 90 fat_vertex
3 -0.75 180 fat_vertex
3 -0.75 270 fat_vertex
0 0 extra_fat_triangle
2 0 extra_fat_triangle_left
0.5 -2 extra_fat_triangle
1.5 -1.3 extra_fat_vertex
2.5 -2 extra_fat_triangle_left
3 -0.75 extra_fat_vertex
grestore
%eof
What is curious is that the .pdf only displays incorrectly when it is resized to certain specific sizes (or: it displays correctly at certain sizes). I don't seem to be able to predict when it will work.
Here is how it displays (part of the image) correctly:
And here it is resized and displaying incorrectly:
(source: dannyc at math.uchicago.edu)
Note that these images are .jpgs obtained from the .pdf. If you save the code above as an .eps file and open it with Preview on a Mac, I assume it will exhibit the same pathology it did on my computer.
Any advice would be very welcome. I don't know enough (or: anything) about how bezier curves are displayed in .pdf to know what might be the problem here.
Here's a cut down case that displays the same rendering problem:
%!PS-Adobe-2.0 EPSF-2.0
%%BoundingBox: 0 0 400 200
gsave 50 50 scale 1 3 translate
0 setgray
0.05 setlinewidth
newpath
0 0 moveto
0 1 2 -1 2 0 curveto
stroke
grestore
%eof
Can't get much simpler than that: a Bezier path with just two points, all with integer values.
It looks like Apple's renderer has some numeric instability around the center of the curve. If you tweak one of the curve control points by a tiny amount, the bug doesn't happen:
0 1.00001 2 -1 2 0 curveto
So there's a workaround: In your original file, add a small fudge factor (a fraction of a degree) to one of the angles in each curve.
I changed each /angle1 exch def to 0.01 add /angle1 exch def, and it worked for me. Here's the EPS file and the resulting PDF file.
(Of course this shouldn't be necessary, but until you file a bug and Apple releases a fix, it may be the most expedient solution.)

Resources