You want to remove the heart beat signal and keep the "noise". We can solve this problem by using a denoising algorithm, and subtracting the denoised signal from the original signal.
Setting frequency cutoffs for a frequency domain filter can get tricky and turn into a game of whack-a-mole because there's "high frequency" components in the heartbeat blip (due to the sudden rise and fall) and also in the wiggly stuff between a heart beat's T wave and the next heart beat's P wave.
Loosely speaking, the requirements in this de-noising problem are as follows:
- Remove the little wiggles
- Maintain larger jumps that appear in a heart beat PQRST waveform
This sounds like a great place to apply $\ell_1$ denoising or total variation denoising. The idea is to approximate the given signal $y$ with a signal $x$ such that the derivative of $x$ is "sparse" i.e. it doesn't change too frequently, but when it changes, the change is large. The denoised estimate $\hat x$ is given by:
$$
\hat x = \min_x ||y-x||_2^2 +\lambda\sum|x_i-x_{i-1}|
$$
I used proxTV toolbox in Python to solve this optimization problem.
import scipy.io as sio
import numpy as np
import matplotlib.pyplot as plt
import prox_tv as ptv
mat_struct = sio.loadmat('Signal1.mat')
noisy_signal = mat_struct['x'].T[0]
filtered_signal = ptv.tv1_1d(noisy_signal, 50)
time_vec = np.linspace(0, len(noisy_signal)/1500., len(noisy_signal))
plt.close('all')
fig, ax = plt.subplots(3,1,sharex=True)
ax[0].plot(time_vec,noisy_signal)
ax[0].set_title('noisy signal')
ax[1].plot(time_vec,filtered_signal)
ax[1].set_title('filtered signal')
ax[2].plot(time_vec,noisy_signal - filtered_signal)
ax[2].set_title('noise')
ax[2].set_xlabel('time (s)')
plt.tight_layout()
plt.show(block=False)
And here's the resulting plot:

Of course, there's a different kind of whack-a-mole you'll have to play with this technique: the $\lambda$ parameter which I set to $50$ in the code.