Well, you need to understand what you are doing with each of these variables. Your code is kind of weird as all things you need to known are kind of hidden somewhere.
Based on your time variable Z, your sampling frequency is 1 ./ diff(Z).
>> fs = 1./diff(Z);
>> fs = fs(1)
And your signal length (FFT size by default) is nZ = 501. Your frequency resolution is thus fs / nZ = 13.3067.
With such a frequency resolution, you cannot expect to identify your target signal 14.5, 16, or 18 Hz. And when it is for 20 Hz, it jumped to the second trace 26.6, and so on.
Your signal Y also has a fraction number of cycles. So you can expect some spectral leakage around your target frequency too.