Actually, pandas is not required here, outer product from numpy will suffice and should be faster to compute the weighted (unbiased) sample covariance matrix, using just couple of lines of code:
mu = np.sum(weights.reshape(-1,1)*X, axis=0) / np.sum(weights)
Sigma2 = (weights.reshape(-1,1)*(X-mu)).T@(X-mu) / (weights.sum()-1)
Here $X$ is the data matrix (numpy ndarray) and weights is the vector of weights, one for each observation (row) in $X$.
We can test from the next code snippet that it produces the same result and is faster (at least with a small synthetic data):
with numpy only:
from time import time
import numpy as np
assuming that the same data is stored as matrix
np.random.seed(1)
X1 = np.random.randint(0,100,size=(100, 4))
weights = np.random.randint(0,100,size=100)
start = time()
mu = np.sum(weights.reshape(-1,1)X1, axis=0) / np.sum(weights)
Sigma2 = (weights.reshape(-1,1)(X1-mu)).T@(X1-mu) / (weights.sum()-1)
print('time taken', time() - start)
Sigma2
time taken 0.00099945068359375
array([[ 890.94399402, -85.64453306, 115.59965823, 27.03209622],
[ -85.64453306, 930.63865593, -219.27393837, -147.81191515],
[ 115.59965823, -219.27393837, 752.47415657, 92.31686826],
[ 27.03209622, -147.81191515, 92.31686826, 745.7993326 ]])
with pandas and numpy:
import pandas as pd
# assuming that the data is stored as dataframe
X = pd.DataFrame(X1, columns=list('ABCD'))
weights = np.random.randint(0,100,size=100)
start = time()
mean = np.ma.average(X, axis=0, weights=weights) # Computing the weighted sample mean (fast, efficient and precise)
mean = pd.Series(mean, index=list(X.keys())) # Convert to a Pandas' Series (it's just aesthetic and more ergonomic, no differenc in computed values)
xm = X-mean # xm = X diff to mean
xm = xm.fillna(0)
sigma2 = 1./(weights.sum()-1) * xm.mul(weights, axis=0).T.dot(xm);
print('time taken', time() - start)
sigma2
time taken 0.0059969425201416016
A B C D
A 890.943994 -85.644533 115.599658 27.032096
B -85.644533 930.638656 -219.273938 -147.811915
C 115.599658 -219.273938 752.474157 92.316868
D 27.032096 -147.811915 92.316868 745.799333