human
I am working on the file "Sessions/Session11/Day2/MeasuringCentroidsAndProperMotionSolutions.ipynb".
The first task is:
(b) Write a function aperture_centroid(img,R) that will return the values of $(y0,x0)$ that satisfy 
$$ M_x = \sum_{xy} I_{xy} W(x-x0,y-y0) (x-x0)=0$$
(and the same for y0), where $W$ is a weight function. I provide for you below a class GaussAp which you can use to create a Gaussian-aperture weight function with some $\sigma_w$:
$$ W(x,y) = \exp{\left(-\frac{x^2+y^2}{2\sigma_w^2}\right)}$$
To do this, you'll need to start with an initial guess for $(y0,x0),$ and then calculate the values of $M_f, M_x$ and $M_y$.  Then move the aperture's $x_0,y_0$ by $(M_x/M_f, M_y/M_f)$ and try again.  Iterate until the centroid doesn't move any more (or until you have done 10 iterations and need to quit!).
Can you write Python program for it?
 ... Write me a Python program to calculate the values of $M_f, M_x$ and $M_y$?
assistant
#Here is Python program to calculate the values of $M_f, M_x$ and $M_y$.

# Here's my weight function class
# Remember, to use a class, you first create an "instance"
# of it, e.g.:  weight = GaussAp(sigma=2.6)
# and then you can call this instance like a function of dx,dy

class GaussAp:
    # A tophat weight function of radius sigma
    def __init__(self,sigma):
        self.sigma = sigma
        return
    def __call__(self,dx,dy):
        # Given equal-shaped arrays dx=x-x0, dy=y-y0,
        # returns an array of the same shape giving weight function.

        # Calculate distance of a pixel from the center
        rsq = np.square(dx) + np.square(dy)
    
        # Now return the weight
        return np.exp(-rsq/(2.*self.sigma*self.sigma))

# Now a function that calculates the weighted moments
def weighted_moments(img, weight, x, y, x0, y0):
    # img is the input image
    # weight is a weight function
    # x,y are the coordinates of the image
    # x0, y0 are the centers for the weight function
    w = weight(x-x0,y-y0)
    mf = np.sum(img*w)
    mx = np.sum(img*w*(x-x0))
    my = np.sum(img*w*(y-y0))
    return mf,mx,my

# Now my function that will iterate over x0,y0 to converge
def aperture_centroid(img, x, y, weight):
    # Iteratively apply the weighted centroiding to the
    # image (with coordinate arrays x,y given) until
    # convergence, where the Mx and My moments are nulled.
    
    # Start at the center of the image
    x0 = 0.5*(img.shape[1]-1)
    y0 = 0.5*(img.shape[0]-1)
    
    iteration = 0
    MAX_ITERATIONS = 20
    tolerance = 0.001 # Quit when centroid moves less than this
    
    while iteration < MAX_ITERATIONS:
        mf,mx,my = weighted_moments(img,weight,x,y,x0,y0)
        dx = mx / mf
        dy = my / mf
        if abs(dx)<tolerance and abs(dy)<tolerance:
            # Our moments are now close to zero - done!
            return np.array((y0,x0))
        
        # otherwise update centroid and try again
        x0 = x0 + dx
        y0 = y0 + dy
        iteration = iteration+1
        
    # If we get here, we've exceeded our iteration...