diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..6339ca3 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,36 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..089f042 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,37 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python package + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics diff --git a/STMiner/SPFinder.py b/STMiner/SPFinder.py index ffe2168..53dbce0 100644 --- a/STMiner/SPFinder.py +++ b/STMiner/SPFinder.py @@ -30,21 +30,21 @@ class SPFinder: def __init__(self, adata: Optional[AnnData] = None): self.adata = None self.patterns = None - self.patterns_matrix_dict = {} - self.patterns_binary_matrix_dict = {} self.genes_distance_array = None self.filtered_distance_array = None self.genes_labels = None self.kmeans_fit_result = None self.mds_features = None self.image_gmm = None - self.csr_dict = {} self.global_distance = None self.all_labels = None - self._gene_expression_edge = {} + self.csr_dict = {} + self.patterns_matrix_dict = {} + self.patterns_binary_matrix_dict = {} + self.plot = Plot(self) self._highly_variable_genes = [] + self._gene_expression_edge = {} self._scope = () - self.plot = Plot(self) # self.app = App() if adata is not None: self.set_adata(adata) @@ -279,7 +279,7 @@ def build_distance_array(self, method="gmm", gene_list=None): self.csr_dict, gene_list ) - def get_pattern_array(self, vote_rate=0.2): + def get_pattern_array(self, vote_rate=0): self.patterns_binary_matrix_dict = {} label_list = set(self.genes_labels["labels"]) for label in label_list: @@ -311,7 +311,6 @@ def cluster_gene( mds_components=20, use_highly_variable_gene=False, n_top_genes=500, - gene_list=None, ): # TODO: genes_labels should be int not float if use_highly_variable_gene: diff --git a/STMiner/test/generate_mds_html.py b/STMiner/test/generate_mds_html.py deleted file mode 100644 index e5ce4b3..0000000 --- a/STMiner/test/generate_mds_html.py +++ /dev/null @@ -1,47 +0,0 @@ -import plotly -# for data visualization -import plotly.express as px -# for creating a swiss roll -from sklearn.datasets import make_swiss_roll - -# Make a swiss roll -X, y = make_swiss_roll(n_samples=2000, noise=0.05) -# Make it thinner -X[:, 1] *= .5 - -# Create a 3D scatter plot -fig = px.scatter_3d(None, x=X[:, 0], y=X[:, 1], z=X[:, 2], color=y, ) - -# Update chart looks -fig.update_layout( - showlegend=False, - scene_camera=dict(up=dict(x=0, y=0, z=1), - center=dict(x=0, y=0, z=-0.1), - eye=dict(x=1.25, y=1.5, z=1)), - margin=dict(l=0, r=0, b=0, t=0), - scene=dict(xaxis=dict(backgroundcolor='white', - color='black', - gridcolor='#f0f0f0', - title_font=dict(size=10), - tickfont=dict(size=10), - ), - yaxis=dict(backgroundcolor='white', - color='black', - gridcolor='#f0f0f0', - title_font=dict(size=10), - tickfont=dict(size=10), - ), - zaxis=dict(backgroundcolor='lightgrey', - color='black', - gridcolor='#f0f0f0', - title_font=dict(size=10), - tickfont=dict(size=10), - ))) - -# Update marker size -fig.update_traces(marker=dict(size=3, line=dict(color='black', width=0.1))) - -fig.update(layout_coloraxis_showscale=False) -fig.update_layout(width=400, height=300) - -plotly.offline.plot(fig, filename='mds.html', image_width=400, image_height=300) diff --git a/STMiner/test/test_10x_data.py b/STMiner/test/test_10x_data.py deleted file mode 100644 index 9961f31..0000000 --- a/STMiner/test/test_10x_data.py +++ /dev/null @@ -1,24 +0,0 @@ -# %% -from STMiner.SPFinder import SPFinder -from STMiner.test.SPFinderTester import SPFinderTester - -# %% -spf = SPFinder() -spf.read_h5ad(file='F://Rep11_MOB_ST.h5ad', amplification=1000, bin_size=80) - -# %% -spf.normalize() -spf.fit_pattern(100, n_comp=10) -spf.cluster_gene(6) - -# %% -spf.genes_labels - -# %% -spft = SPFinderTester() -spft.read_h5ad(file='F://Rep11_MOB_ST.h5ad', amplification=1000, bin_size=80) - -# %% -spft.normalize() -spft.fit_pattern(100, n_comp=10) -spft.cluster_gene(6) diff --git a/STMiner/test/test_hotspot.py b/STMiner/test/test_hotspot.py deleted file mode 100644 index 2354398..0000000 --- a/STMiner/test/test_hotspot.py +++ /dev/null @@ -1,63 +0,0 @@ -import hotspot -import pandas as pd -import scanpy as sc -from anndata import AnnData - -from STMiner.IO.IOUtil import merge_bin_coordinate -from STMiner.SPFinder import SPFinder - -# %% -raw_df = pd.read_csv("E:/data/brain10x.csv", - sep=',', - index_col=0) -APP23_1 = raw_df.filter(regex=r'^APP23_I_') -APP23_1 = APP23_1.rename(columns=lambda x: x.replace('APP23_I_', '')) -adata = AnnData(X=APP23_1.T) -position_df = pd.read_csv('E:/data/RegionC/RegionC_10x_immunoloc.csv', sep=',', index_col=0) -# replace '-' to '.', example: 'AAACAAGTATCTCCCA-1' to 'AAACAAGTATCTCCCA.1' -position_df.index = position_df.index.str.replace('-', '.') -position_df.columns = position_df.columns.str.replace('pxl_row_in_immuno', 'x') -position_df.columns = position_df.columns.str.replace('pxl_col_in_immuno', 'y') -adata.obs = position_df.reindex(adata.obs.index) - -# %% -adata.obs['x'] = merge_bin_coordinate(adata.obs['x'], adata.obs['x'].min(), bin_size=80) -adata.obs['y'] = merge_bin_coordinate(adata.obs['y'], adata.obs['y'].min(), bin_size=80) -sc.pp.filter_genes(adata, min_cells=200) -sc.pp.normalize_total(adata, inplace=True) -sc.pp.log1p(adata) -sc.pp.calculate_qc_metrics(adata, inplace=True) - -# %% -sc.tl.pca(adata, svd_solver='arpack') - -# %% -sp = SPFinder(adata) - -# %% -hs = hotspot.Hotspot( - adata, - model='danb', - latent_obsm_key="X_pca", - umi_counts_obs_key="total_counts" -) -# %% -hs.create_knn_graph(weighted_graph=False, n_neighbors=30) - -# %% -hs_results = hs.compute_autocorrelations() -# %% -hs_genes = hs_results.loc[hs_results.FDR < 0.05].index # Select genes - -local_correlations = hs.compute_local_correlations(hs_genes, jobs=1) # jobs for parallelization - -# %% -modules = hs.create_modules( - min_gene_threshold=30, core_only=True, fdr_threshold=0.05 -) -module_scores = hs.calculate_module_scores() - -# %% -module_scores -# %% -hs.plot_local_correlations() diff --git a/STMiner/test/test_noise.py b/STMiner/test/test_noise.py deleted file mode 100644 index 00f0c1a..0000000 --- a/STMiner/test/test_noise.py +++ /dev/null @@ -1,48 +0,0 @@ -import numpy as np -import pandas as pd -from anndata import AnnData - -from STMiner.test.SPFinderTester import SPFinderTester - -count = pd.read_csv('./data/test2/simulate_exp.csv', sep=',', index_col=0) -position = pd.read_csv('./data/test2/simulate_position.csv', sep=',', index_col=0) - -adata = AnnData(X=count) -adata.obs = position.reindex(adata.obs.index) -path = "E://SpatialDE_result/" - -for interval in [20, 15, 10, 5]: - spft = SPFinderTester() - spft.set_adata(adata) - spft.set_noise_output_path(path) - spft.set_noise_type('periodicity', interval) - spft.fit_pattern(n_top_genes=300, n_comp=20) - spft.build_distance_array() - spft.cluster_gene(n_clusters=3, mds_components=30) - result = spft.genes_labels - name = 'periodicity_' + str(interval) + '.csv' - result.to_csv(path + name, sep=',') - -for mean in np.arange(0.1, 1, 0.2, dtype=np.float32): - spft = SPFinderTester() - spft.set_noise_output_path(path) - spft.set_adata(adata) - spft.set_noise_type('gauss', mean) - spft.fit_pattern(n_top_genes=300, n_comp=20) - spft.build_distance_array() - spft.cluster_gene(n_clusters=3, mds_components=30) - result = spft.genes_labels - name = 'gauss_' + str(mean) + '.csv' - result.to_csv(path + name, sep=',') - -for percentage in np.arange(0.1, 1, 0.2, dtype=np.float32): - spft = SPFinderTester() - spft.set_adata(adata) - spft.set_noise_output_path(path) - spft.set_noise_type('sp', percentage) - spft.fit_pattern(n_top_genes=300, n_comp=20) - spft.build_distance_array() - spft.cluster_gene(n_clusters=3, mds_components=30) - result = spft.genes_labels - name = 'sp_' + str(percentage) + '.csv' - result.to_csv(path + name, sep=',') diff --git a/STMiner/test/test_real_data.py b/STMiner/test/test_real_data.py deleted file mode 100644 index a9623c6..0000000 --- a/STMiner/test/test_real_data.py +++ /dev/null @@ -1,18 +0,0 @@ -import pandas as pd -from anndata import AnnData - -from STMiner.SPFinder import SPFinder - -count = pd.read_csv('E://SpatialDE/Analysis/MouseOB/data/Rep11_MOB_0.csv', sep=',', index_col=0) -position = pd.read_csv('E://SpatialDE/Analysis/MouseOB/MOB_sample_info.csv', sep=',', index_col=0) - -adata = AnnData(X=count) -adata.obs = position.reindex(adata.obs.index) - -spf = SPFinder() -spf.set_adata(adata) -spf.merge_bin(bin_width=50, amplification=1000) -spf.normalize() -spf.fit_pattern(n_top_genes=100, n_comp=20) -spf.cluster_gene(n_clusters=3, mds_components=30) -spf.patterns diff --git a/STMiner/test/test_stero_data.py b/STMiner/test/test_stero_data.py deleted file mode 100644 index e69de29..0000000 diff --git a/STMiner/unittest/__init__.py b/STMiner/unittest/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/STMiner/unittest/test_distribution.py b/STMiner/unittest/test_distribution.py deleted file mode 100644 index d29bbf6..0000000 --- a/STMiner/unittest/test_distribution.py +++ /dev/null @@ -1,6 +0,0 @@ -def test_get_hellinger_distance(): - assert True - - -def test_get_helling(): - assert True diff --git a/readme.md b/readme.md index 298fae8..a9a0e7b 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -![Static Badge](https://img.shields.io/badge/License-MIT-blue) +![Static Badge](https://img.shields.io/badge/License-GPL3.0-blue) ![Static Badge](https://img.shields.io/badge/readthedocs-blue?logo=readthedocs&label=Documents) ![Static Badge](https://img.shields.io/badge/3.10-green?logo=python&label=Python&labelColor=yellow) ![Static Badge](https://img.shields.io/badge/Linux-blue?logo=Linux&logoColor=white) @@ -178,6 +178,9 @@ sp.plot.plot_genes(label=0, vmax=99) | genes_labels | pd. DataFrame | Gene name and their pattern labels | | plot | Object | Call plot to visualization | +# Release history +https://pypi.org/project/STMiner/#history + # Contact - Peisen Sun (sunpeisen@stu.xjtu.edu.cn) - Kai Ye (kaiye@xjtu.edu.cn)