Self studying, getting a quality spectrogram
Why are my peaks capped?


Your amplification gain is set to too high, or you are too close to the microphone. The amplifier is driven to its limits and it clips the output. Keep this recording and make another one where you are a little bit further away from the microphone to later compare the differences in the spectrogram. It will be interesting to see how this clipping manifests itself in the frequency spectrum.


  ...how do I get better resolution and clarity so as to read stuff such as formants in vowels, i tried changing the size of my partitions, but still get very similar results, more windows get less data of frequencies in each window, and less windows get poor time resolution. 


On line 7 of your point #6, you make use of numpy.fft.rfft. 

From the documentation ...the length of the transformed axis of the output is therefore n//2 + 1..

Your signal is about 2 seconds long at a sampling frequency (Fs) of 16kHz and you are dividing it into windows of 1000 samples that overlap by 965 samples. That's about 97% overlap.

This 97% overlap is a very high temporal resolution.  What you are lacking is in frequency resolution and the only way to increase this is to increase your window size. This will give the Fast Fourier Transform (FFT) the opportunity to resolve more frequencies.

These are Audacity's parameters for deriving spectrograms:





There are two things to notice here:


All of Audacity's window sizes are powers of 2. This is done to exploit the structure of the calculations in computing the FT and do it F(ast).
The window size is inversely proportional to the resolution of the FFT. A size 8 window is marked as "most wideband" and a size 32768 is marked as "most narrowband". Of course, your window size is not independent of your Sampling Frequency and there are limits to how many frequency components it can resolve given the data that has already been captured at sampling. Therefore, higher true frequency resolution means capturing more data which means increasing your sampling frequency. With 16kHz you can resolve (theoretically) up to 8kHz.


So with the exception of a few details and optimisations here and there, you are down the right track with your code. 

You are missing:


The windowing functionality (nothing to do with the length of the sliding window you apply in your code (1000)). This will become important later on if you want to resolve things like formants.
"Proper" plotting which sets the x and y axes of your plot to the time and frequency scales required to interpret the spectrogram. This is not something incredibly difficult to do, but you will find yourself needing it, otherwise you will be doing "back of the envelope" calculations every time you want to read frequencies and offsets from your plots.


Ultimately, you will add those to your code too and you will have replicated, to a large extent, the functionality of scipy.signal.spectrogram.

Therefore, I would recommend to you to focus your studying efforts on the specifics of the spectrogram in order to understand how it works and what the impact of each one of its parameters is to the quality of its output.

A good introduction to the spectrogram itself is this one. And of course, it is not the only one out there.

scipy.signal.spectrogram includes all of the key parameters and it will allow you to explore spectrograms comfortably.

Finally, if you are interested in audio analysis, there are some really good libraries in the Python space, that implement a lot of functionality you will need. Some examples are pyAudioAnalysis and librosa (and here for something closer to what you are asking). Many more pointers available here.

Hope this helps.