def zip_cluster(data, k, init=None, max_iters=<NUM_LIT:100>):
genes, cells = data.shape<EOL>init, new_assignments = kmeans_pp(data+eps, k, centers=init)<EOL>centers = np.copy(init)<EOL>M = np.zeros(centers.shape)<EOL>assignments = new_assignments<EOL>for c in range(k):<EOL><INDENT>centers[:,c], M[:,c] = zip_fit_params_mle(data[:, assignments==c])<EOL><DEDENT>for it in range(max_iters):<EOL><INDENT>lls = zip_ll(data, centers, M)<EOL>new_assignments = np.argmax(lls, <NUM_LIT:1>)<EOL>if np.equal(assignments, new_assignments).all():<EOL><INDENT>return assignments, centers, M<EOL><DEDENT>for c in range(k):<EOL><INDENT>centers[:,c], M[:,c] = zip_fit_params_mle(data[:, assignments==c])<EOL><DEDENT>assignments = new_assignments<EOL><DEDENT>return assignments, centers, M<EOL>
Performs hard EM clustering using the zero-inflated Poisson distribution.

Args:
    data (array): A 2d array- genes x cells
    k (int): Number of clusters
    init (array, optional): Initial centers - genes x k array. Default: None, use kmeans++
    max_iters (int, optional): Maximum number of iterations. Default: 100

Returns:
    assignments (array): integer assignments of cells to clusters (length cells)
    L (array): Poisson parameter (genes x k)
    M (array): zero-inflation parameter (genes x k)