|
| 1 | +import os.path |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +import pandas as pd |
| 5 | +import matplotlib.pyplot as plt |
| 6 | +import seaborn as sns |
| 7 | + |
| 8 | +from scipy.spatial import cKDTree |
| 9 | + |
| 10 | +res = 0.2201 # um per pixel |
| 11 | +radii_in_um = 32 |
| 12 | +radii = [radii_in_um // res] |
| 13 | + |
| 14 | +sample_size = [-1, -1] |
| 15 | + |
| 16 | +def getCoords(data:pd.DataFrame): |
| 17 | + centroid = data['Centroid'] |
| 18 | + centroid = centroid.apply(lambda x: x[1:-1].split(',')) |
| 19 | + |
| 20 | + xs = centroid.apply(lambda x: np.float32(x[0])) |
| 21 | + ys = centroid.apply(lambda x: np.float32(x[1])) |
| 22 | + return np.array(xs), np.array(ys) |
| 23 | + |
| 24 | + |
| 25 | +def getAllCoords(root, slide_name): |
| 26 | + cell_coords = {} |
| 27 | + for cell_type in ['I', 'S', 'T']: |
| 28 | + csv_file = os.path.join(root, f'{slide_name}_Feats_{cell_type}.csv') |
| 29 | + df = pd.read_csv(csv_file) |
| 30 | + xs, ys = getCoords(df) |
| 31 | + cell_coords[cell_type] = (xs, ys) |
| 32 | + sample_size[0], sample_size[1] = max(sample_size[0], max(xs)), max(sample_size[1], max(ys)) |
| 33 | + return cell_coords |
| 34 | + |
| 35 | + |
| 36 | +def getRelation(coords:dict, cell_types:list): |
| 37 | + assert sample_size[0] != -1, 'sample_size should be set' |
| 38 | + k = [] |
| 39 | + if len(cell_types) == 2: |
| 40 | + for radius in radii: |
| 41 | + counts = 0 |
| 42 | + score_vol = np.pi * radius**2 |
| 43 | + bound_size = sample_size[0] * sample_size[1] |
| 44 | + alpha_x, alpha_y = coords[cell_types[0]][0], coords[cell_types[0]][1] |
| 45 | + beta_x, beta_y = coords[cell_types[1]][0], coords[cell_types[1]][1] |
| 46 | + tree = cKDTree(np.array([alpha_x, alpha_y]).T) |
| 47 | + for x, y in zip(beta_x, beta_y): |
| 48 | + # boundary_correct = False |
| 49 | + counts += len(tree.query_ball_point([x, y], radius, p=2))-1 |
| 50 | + # CSR_Normalise |
| 51 | + # k_value = bound_size * counts / len(beta_x)**2 - score_vol |
| 52 | + # estimation |
| 53 | + k_value = counts / len(beta_x) |
| 54 | + k.append(k_value) |
| 55 | + else: |
| 56 | + raise ValueError('cell_types should be a list of 2') |
| 57 | + return k |
| 58 | + |
| 59 | + |
| 60 | +if __name__ == '__main__': |
| 61 | + # allCoords = getAllCoords(r'C:\Users\Ed\Downloads\temp', '2023-31276') |
| 62 | + slide_name = 'TCAM' # '2023-31276' |
| 63 | + allCoords = getAllCoords(r'C:\Users\Ed\Downloads\temp', slide_name) |
| 64 | + # I_S = getRelation(allCoords, ['I', 'S']) |
| 65 | + |
| 66 | + plt.figure(figsize=(24, 18)) |
| 67 | + plt.scatter(allCoords['S'][0], allCoords['S'][1], c='blue', alpha=0.5, s=1) |
| 68 | + plt.scatter(allCoords['I'][0], allCoords['I'][1], c='green', alpha=0.5, s=1) |
| 69 | + plt.scatter(allCoords['T'][0], allCoords['T'][1], c='red', alpha=0.5, s=1) |
| 70 | + |
| 71 | + plt.rcParams['font.family'] = 'Times New Roman' |
| 72 | + |
| 73 | + plt.xlabel('X Coordinates') |
| 74 | + plt.ylabel('Y Coordinates') |
| 75 | + plt.title('Scatter plot of Centroids') |
| 76 | + plt.gca().invert_yaxis() |
| 77 | + plt.savefig(f'{slide_name}_scatter.png') |
| 78 | + |
| 79 | + print(f'Cell \t Cell \t RipleyK') |
| 80 | + # print(f'I \t I \t {getRelation(allCoords, ["I"])}') |
| 81 | + # print(f'S \t S \t {getRelation(allCoords, ["S"])}') |
| 82 | + # print(f'T \t T \t {getRelation(allCoords, ["T"])}') |
| 83 | + # |
| 84 | + # print(f'I \t S \t {getRelation(allCoords, ["I", "S"])}') |
| 85 | + # print(f'I \t T \t {getRelation(allCoords, ["I", "T"])}') |
| 86 | + # |
| 87 | + # print(f'S \t I \t {getRelation(allCoords, ["S", "I"])}') |
| 88 | + # print(f'S \t T \t {getRelation(allCoords, ["S", "T"])}') |
| 89 | + # |
| 90 | + # print(f'T \t I \t {getRelation(allCoords, ["T", "I"])}') |
| 91 | + # print(f'T \t S \t {getRelation(allCoords, ["T", "S"])}') |
| 92 | + |
| 93 | + cell_types = ['I', 'S', 'T'] |
| 94 | + matrix = np.zeros((len(cell_types), len(cell_types), len(radii))) |
| 95 | + |
| 96 | + for i, cell_type_a in enumerate(cell_types): |
| 97 | + for j, cell_type_b in enumerate(cell_types): |
| 98 | + matrix[i, j, :] = getRelation(allCoords, [cell_type_a, cell_type_b]) |
| 99 | + print(f'{cell_type_a} \t {cell_type_b} \t {matrix[i, j, :]}') |
| 100 | + |
| 101 | + for idx, radius in enumerate(radii): |
| 102 | + plt.figure(figsize=(6.5, 6)) |
| 103 | + sns.heatmap(matrix[:, :, idx], annot=True, fmt=".2f", xticklabels=cell_types, yticklabels=cell_types, cmap="seismic") |
| 104 | + plt.title(f"Distribution of expectations ({radii_in_um} μm)") |
| 105 | + plt.xlabel("Cell Type B (surroundings)") |
| 106 | + plt.ylabel("Cell Type A (targets)") |
| 107 | + plt.savefig(f'{slide_name}_heatmap_radius_{radius}.png') |
| 108 | + plt.close() |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | + |
0 commit comments