X1 and X are both pointers. When you use X1=X, you assign the address of X to X1 and hence any change in X is reflected in X1. It will happen with array, list, dictionary data structures too. There are a couple of ways you can try.
Approach 1: Use copy module
>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>> from copy import deepcopy
>>> row = np.array([0, 0, 1, 2, 2, 2,3,4,4])
>>> col = np.array([0, 2, 2, 0, 1, 2,2,0,2])
>>> data = np.array([1]*len(row))
>>> X=csr_matrix((data, (row, col)), shape=(5, 3))
>>> X
<5x3 sparse matrix of type '<type 'numpy.int32'>'
with 9 stored elements in Compressed Sparse Row format>
>>> X.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X1=deepcopy(X)
>>> X1.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X[1]=[1,2,3]
>>> X.toarray()
array([[1, 0, 1],
[1, 2, 3],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X1.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
Approach 2: Using ':'
>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>> row = np.array([0, 0, 1, 2, 2, 2,3,4,4])
>>> col = np.array([0, 2, 2, 0, 1, 2,2,0,2])
>>> data = np.array([1]*len(row))
>>> X=csr_matrix((data, (row, col)), shape=(5, 3))
>>> X.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X1=X[:]
>>> X1.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X[1]=[1,2,3]
>>> X.toarray()
array([[1, 0, 1],
[1, 2, 3],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])
>>> X1.toarray()
array([[1, 0, 1],
[0, 0, 1],
[1, 1, 1],
[0, 0, 1],
[1, 0, 1]])