SpatialDE icon indicating copy to clipboard operation
SpatialDE copied to clipboard

Multi-dimensional indexing (e.g. `obj[:, None]`) is no longer supported.

Open Scoott opened this issue 1 year ago • 3 comments

I get the following error running the scanpy spatial transcriptomics tutorial with the following command:

counts = pd.DataFrame(adata.X.todense(), columns=adata.var_names, index=adata.obs_names)
coord = pd.DataFrame(adata.obsm['spatial'], columns=['x_coord', 'y_coord'], index=adata.obs_names)
results = SpatialDE.run(coord, counts)

Traceback:

ValueError Traceback (most recent call last) File :3

File /opt/homebrew/lib/python3.11/site-packages/SpatialDE/base.py:420, in run(X, exp_tab, kernel_space) 411 ''' Perform SpatialDE test 412 413 X : matrix of spatial coordinates times observations (...) 417 model can be specifiec using the kernel_space paramter. 418 ''' 419 if kernel_space == None: --> 420 l_min, l_max = get_l_limits(X) 421 kernel_space = { 422 'SE': np.logspace(np.log10(l_min), np.log10(l_max), 10), 423 'const': 0 424 } 426 logging.info('Performing DE test')

File /opt/homebrew/lib/python3.11/site-packages/SpatialDE/base.py:26, in get_l_limits(X) 24 def get_l_limits(X): 25 Xsq = np.sum(np.square(X), 1) ---> 26 R2 = -2. * np.dot(X, X.T) + (Xsq[:, None] + Xsq[None, :]) 27 R2 = np.clip(R2, 0, np.inf) 28 R_vals = np.unique(R2.flatten())

File /opt/homebrew/lib/python3.11/site-packages/pandas/core/series.py:1072, in Series.getitem(self, key) 1069 key = np.asarray(key, dtype=bool) 1070 return self._get_rows_with_mask(key) -> 1072 return self._get_with(key)

File /opt/homebrew/lib/python3.11/site-packages/pandas/core/series.py:1082, in Series._get_with(self, key) 1077 raise TypeError( 1078 "Indexing a Series with DataFrame is not " 1079 "supported, use the appropriate DataFrame column" 1080 ) 1081 elif isinstance(key, tuple): -> 1082 return self._get_values_tuple(key) 1084 elif not is_list_like(key): 1085 # e.g. scalars that aren't recognized by lib.is_scalar, GH#32684 1086 return self.loc[key]

File /opt/homebrew/lib/python3.11/site-packages/pandas/core/series.py:1122, in Series._get_values_tuple(self, key) 1117 if com.any_none(*key): 1118 # mpl compat if we look up e.g. ser[:, np.newaxis]; 1119 # see tests.series.timeseries.test_mpl_compat_hack 1120 # the asarray is needed to avoid returning a 2D DatetimeArray 1121 result = np.asarray(self._values[key]) -> 1122 disallow_ndim_indexing(result) 1123 return result 1125 if not isinstance(self.index, MultiIndex):

File /opt/homebrew/lib/python3.11/site-packages/pandas/core/indexers/utils.py:341, in disallow_ndim_indexing(result) 333 """ 334 Helper function to disallow multi-dimensional indexing on 1D Series/Index. 335 (...) 338 in GH#30588. 339 """ 340 if np.ndim(result) > 1: --> 341 raise ValueError( 342 "Multi-dimensional indexing (e.g. obj[:, None]) is no longer " 343 "supported. Convert to a numpy array before indexing instead." 344 )

ValueError: Multi-dimensional indexing (e.g. obj[:, None]) is no longer supported. Convert to a numpy array before indexing instead.

Scoott avatar Nov 03 '23 13:11 Scoott

the same ,have you fix it ?

speed090 avatar Nov 12 '23 07:11 speed090

@speed090 I took a look at it today and fixed the issue. From your python installation, change the following code in (whichever location/version) python3.11/site-packages/SpatialDE/base.py.

FROM:

def get_l_limits(X): Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, None] + Xsq[None, :]) R2 = np.clip(R2, 0, np.inf) R_vals = np.unique(R2.flatten()) R_vals = R_vals[R_vals > 1e-8] l_min = np.sqrt(R_vals.min()) / 2. l_max = np.sqrt(R_vals.max()) * 2. return l_min, l_max

Kernels

def SE_kernel(X, l): Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, None] + Xsq[None, :]) R2 = np.clip(R2, 1e-12, np.inf) return np.exp(-R2 / (2 * l ** 2))

def linear_kernel(X): K = np.dot(X, X.T) return K / K.max()

def cosine_kernel(X, p): ''' Periodic kernel as l -> oo in [Lloyd et al 2014] Easier interpretable composability with SE? ''' Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, None] + Xsq[None, :]) R2 = np.clip(R2, 1e-12, np.inf) return np.cos(2 * np.pi * np.sqrt(R2) / p)

TO:

def get_l_limits(X): X = np.array(X) Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, np.newaxis] + Xsq[np.newaxis, :]) R2 = np.clip(R2, 0, np.inf) R_vals = np.unique(R2.flatten()) R_vals = R_vals[R_vals > 1e-8] l_min = np.sqrt(R_vals.min()) / 2. l_max = np.sqrt(R_vals.max()) * 2. return l_min, l_max

Kernels

def SE_kernel(X, l): X = np.array(X) Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, np.newaxis] + Xsq[np.newaxis, :]) R2 = np.clip(R2, 1e-12, np.inf) return np.exp(-R2 / (2 * l ** 2))

def linear_kernel(X): K = np.dot(X, X.T) return K / K.max()

def cosine_kernel(X, p): ''' Periodic kernel as l -> oo in [Lloyd et al 2014] Easier interpretable composability with SE? ''' X = np.array(X) Xsq = np.sum(np.square(X), 1) R2 = -2. * np.dot(X, X.T) + (Xsq[:, np.newaxis] + Xsq[np.newaxis, :]) R2 = np.clip(R2, 1e-12, np.inf) return np.cos(2 * np.pi * np.sqrt(R2) / p)`

The changes I made in the functions first explicitly casts the input 'X' as a np.array. I also changed the indexing using 'None' type to 'np.newaxis'.

Scoott avatar Nov 13 '23 13:11 Scoott

Thanks @Scoott, this worked for me. Submitted it as a PR but this repo seems to be not actively maintained. If anyone wants to install a version with this fix implemented, you can clone this fork (https://github.com/imran-aifi/SpatialDE) and pip install -e SpatialDE/Python-module/

imran-aifi avatar Feb 20 '24 23:02 imran-aifi