<jupyter_start><jupyter_text>Import Data<jupyter_code>data = sc.read_loom('../data/osmFISH_SScortex_mouse_all_cells.loom')
df = data.obs
df.head()
df = df[df['ClusterID'] != 0] #subset to the included cells.
df.reset_index(inplace=True)
df.head()
cell_type_names = np.array(df['ClusterName'].unique())
n_cell_types = len(cell_type_names)
print("%s unique cell types" % n_cell_types)
#Remap cell type IDs because they are not continuous for some reason

new_celltype_id_dict = dict(zip(cell_type_names, [x for x in range(0, len(cell_type_names))]))
df['Cluster_remap'] = df['ClusterName'].apply(lambda x: new_celltype_id_dict[x])
cell_type_names = np.array(df['Cluster_remap'].unique())

cmap = cm.get_cmap('nipy_spectral', n_cell_types)

color_list = []
for i in range(cmap.N):
    rgb = cmap(i)[:3] # will return rgba, we take only first 3 so we get rgb
    #print(colors.rgb2hex(rgb))
    color_list.append(colors.rgb2hex(rgb))
type_color_dict = dict(zip(cell_type_names, color_list))
df['cell_type_color'] = df['Cluster_remap'].apply(lambda x: type_color_dict[x])
reg_names = np.array(df['Region'].unique())
n_regions = len(reg_names)
print("%s unique region labels" % n_regions)

cmap = cm.get_cmap('nipy_spectral', n_regions)

color_list = []
for i in range(cmap.N):
    rgb = cmap(i)[:3] # will return rgba, we take only first 3 so we get rgb
    #print(colors.rgb2hex(rgb))
    color_list.append(colors.rgb2hex(rgb))

reg_color_dict = dict(zip(reg_names, color_list))
df['reg_color'] = df['Region'].apply(lambda x: reg_color_dict[x])
plt.clf()
fix, (ax1, ax2) = plt.subplots(1,2, figsize = (8,4))
ax1.scatter(df['X'], df['Y'], color=df['reg_color'], marker='o', s=1)
ax1.set_title('colored by region label')
ax1.set_ylim(50000,0)
ax2.scatter(df['X'], df['Y'], color=df['cell_type_color'], marker='o', s=1)
ax2.set_title('colored by cell type cluster')
ax2.set_ylim(50000,0)
#plt.gca().invert_yaxis()
plt.savefig('../fig/full_data_region_type_sidebyside.pdf')
plt.tight_layout()
plt.show()
reg_names
import matplotlib
from matplotlib import cm, colors
import matplotlib.pyplot as plt

matplotlib.rcParams.update({'axes.linewidth': 0.25,
                            'xtick.major.size': 2,
                            'xtick.major.width': 0.25,
                            'ytick.major.size': 2,
                            'ytick.major.width': 0.25,
                            'pdf.fonttype': 42,
                            'font.sans-serif': 'Arial'})

reg_names = np.array(df['Region'].unique())
n_regions = len(reg_names)
print("%s unique region labels" % n_regions)

cmap = cm.get_cmap('tab20', n_regions + 5)

'''color_list = []
for i in range(cmap.N):
    rgb = cmap(i)[:3] # will return rgba, we take only first 3 so we get rgb
    #print(colors.rgb2hex(rgb))
    color_list.append(colors.rgb2hex(rgb))'''

color_list = ['#CC6677', '#AA4599', '#882156','#E5A024', '#269E73', '#F0E441',
              '#D36027', '#CD79A7', '#20783C', '#89CCED', '#DDCB76', 
              ] #these are pretty colorblind friendly.
np.random.shuffle(color_list)
reg_color_dict = dict(zip(reg_names, color_list))
df['reg_color'] = df['Region'].apply(lambda x: reg_color_dict[x])


plt.clf()
fix, (ax1) = plt.subplots(1,1, figsize = (10,8))

for reg_name in reg_names:
    df_r = df[df['Region'] == reg_name]
    ax1.scatter(df_r['X'], df_r['Y'], color=df_r['reg_color'], marker='o', s=3, label=str(reg_name))
ax1.set_ylim(50000,0)
# Shrink current axis by 20%
box = ax1.get_position()
ax1.set_position([box.x0, box.y0, box.width * 0.8, box.height])

# Put a legend to the right of the current axis
ax1.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.savefig('../fig/FigureS4B.pdf')<jupyter_output>11 unique region labels<jupyter_text>Make region data<jupyter_code>def calculate_tiles(min_x, max_x, min_y, max_y, tile_size):
    L = max_x - min_x
    W = max_y - min_y
    l = np.ceil(L/tile_size)
    w = np.ceil(W/tile_size)
    print(l, w)
    print(l*w)
    return int(l), int(w)
tile_size = 2000

min_x = int(np.floor(min(df['X'])))
max_x = int(np.ceil(max(df['X'])))

min_y = int(np.floor(min(df['Y'])))
max_y = int(np.ceil(max(df['Y'])))

l, w = calculate_tiles(min_x, max_x, min_y, max_y, tile_size)
x_bounds = [i for i in range(min_x, max_x+tile_size-1, tile_size)]
y_bounds = [i for i in range(min_y, max_y+tile_size-1, tile_size)]

tile_types_needed=[]

for i in range(1, len(x_bounds)):
    for j in range(1, len(y_bounds)):
        min_x = int(np.round(x_bounds[i - 1]))
        max_x = int(np.round(x_bounds[i]))
        min_y = int(np.round(y_bounds[j - 1]))
        max_y = int(np.round(y_bounds[j]))

        #Subset the dataframe 
        temp = df[(df['X'] >= min_x) & (df['X'] < max_x) & 
                  (df['Y'] >= min_y) & (df['Y'] < max_y)]
        
        if len(temp)> 2:
            values, counts = np.unique(np.array(temp['Region']), return_counts=True)
            tile_type = values[np.argmax(counts)]
        else:
            tile_type= 'null'
        tile_types_needed.append(tile_type)

tile_values, tile_counts = np.unique(tile_types_needed, return_counts=True)
region_id_dict = dict(zip(tile_values, [i for i in range(0, len(tile_values))]))
#region_id_dict['null'] = 0 
import matplotlib

plt.clf()
fix, (ax1, ax2) = plt.subplots(1,2, figsize = (12,6))
ax1.scatter(df['X'], df['Y'], color=df['reg_color'], marker='o', s=2)
ax1.hlines(y_bounds,min(x_bounds), max(x_bounds))
ax1.vlines(x_bounds,min(y_bounds), max(y_bounds))
ax1.set_ylim(50000,0)
ax1.set_title('colored by brain region')

patches=[]
for i in range(0, len(tile_types_needed)):
    tile_type = tile_types_needed[i]
    if tile_type != 'null':
        color = reg_color_dict[tile_type]
    else:
        color = '#FFFFFF'
    
    j = int(np.floor(i/w))
    k = int(i % w)
    
    #y_hi = y_bounds[k+1] - y_bounds[0]
    y_low = y_bounds[k] - y_bounds[0]
    x_low = x_bounds[j] - x_bounds[0]
    
    r = plt.Rectangle((x_low,y_low), x_bounds[1]- x_bounds[0], y_bounds[1]- y_bounds[0], facecolor=color)
    
    patches.append(r)

p = matplotlib.collections.PatchCollection(patches, match_original=True)
ax2.add_collection(p)
ax2.set_ylim(50000,0)
ax2.set_xlim(0,25000)
    
#plt.savefig('../fig/' + dataset_name + '_full_data_by_region.pdf')
plt.tight_layout()
plt.show()<jupyter_output><empty_output><jupyter_text>Reconstruct network of cells<jupyter_code>from scipy import spatial, sparse
points = np.array([(df['X'][i], df['Y'][i]) for i in df.index])
point_tree = spatial.cKDTree(points) #efficient search structure
def find_neighbors(start_point, search_radius, point_tree):
    '''
    Returns
    -------
    list of indicies in points that are within search_radius of start_point.
    '''
    return point_tree.query_ball_point(start_point, search_radius)
search_radius = 400 #1 pixel = 0.065μm
neighbors = [find_neighbors(points[i], search_radius, point_tree) for i in range(0, len(points))] 

#Visualize
neighbor_count_dist = [len(neighbors[i]) - 1 for i in range(0, len(neighbors))]
plt.hist(neighbor_count_dist, bins=17, color = 'orange')
plt.show()<jupyter_output><empty_output><jupyter_text>Build Graph<jupyter_code>import networkx as nx
G = nx.Graph() #empty graph
node_ids = [i for i in df.index] #list of node_ids
G.add_nodes_from(node_ids)

#Connect the nodes
for i in range(0, len(node_ids)):
    for j in neighbors[i]:
        if j == i: 
            pass #no self loops
        else:
            G.add_edge(i, j)
sparse.save_npz('../data/osmfish_adjmat_'+str(search_radius) + 'px.npz', nx.to_scipy_sparse_matrix(G))<jupyter_output><empty_output><jupyter_text>Calculate full image parameters<jupyter_code>import sys
sys.path.insert(0, "../../") #this is wild you need to fix this if you show anyone else
from spatialpower.neighborhoods import permutationtest as perm_test
A_full = sparse.load_npz('../data/osmfish_adjmat_400px.npz')
G_full = nx.from_scipy_sparse_matrix(A_full)
def build_assignment_matrix(attribute_dict, n_cell_types):
    data = list(attribute_dict.items())
    data = np.array(data)  # Assignment matrix

    B = np.zeros((data.shape[0], n_cell_types))  # Empty matrix

    for i in range(0, data.shape[0]):
        t = int(data[i, 1])
        B[i, t] = 1

    return B
attribute_dict = dict(zip(df.index, df['Cluster_remap']))
B_full = build_assignment_matrix(attribute_dict, n_cell_types)
p_full = np.divide(np.sum(B_full, axis=0), B_full.shape[0])
H_full = perm_test.calculate_neighborhood_distribution_sparse(A_full, B_full)<jupyter_output>../../spatialpower/neighborhoods/permutationtest.py:170: RuntimeWarning: divide by zero encountered in true_divide
  aux1 = 1/edge_count_by_node<jupyter_text>Calulate region specific parameters<jupyter_code>def calculate_region_params(reg_name, graph, df, save=True, ret_vals=False):
   
    reg_code = reg_name.replace(' ', '-')
    
    region_df = df[df['Region'] == reg_name]
    region_node_ids = [x for x in region_df.index] #the pd dataframe is 1-indexed but the graph is 0-indexed
    region_subgraph = G.subgraph(region_node_ids)
    
    #region_df['ClusterID'] = region_df['cell_type'].apply(lambda x: cell_type_id_dict[x])
    attribute_dict = dict(zip(region_node_ids, region_df['Cluster_remap']))
    
    A = nx.convert_matrix.to_scipy_sparse_matrix(region_subgraph)
    B = build_assignment_matrix(attribute_dict, n_cell_types)
    p = np.divide(np.sum(B, axis=0), A.shape[0])
    H = perm_test.calculate_neighborhood_distribution_sparse(A, B)
    
    if save:
        sparse.save_npz('../data/' + reg_code + '_A_'+ str(search_radius) + 'px.npz', A)
        np.save('../data/' + reg_code + '_B_'+ str(search_radius) + 'px.npy', B)
        np.save('../data/' + reg_code + '_p_'+ str(search_radius) + 'px.npy', p)
        np.save('../data/' + reg_code + '_H_'+ str(search_radius) + 'px.npy', H)
    
    if ret_vals:
        return A, B, p, H
    else:
        return
    
for reg_name in reg_names:
    calculate_region_params(reg_name, G, df, save=True, ret_vals=False)<jupyter_output><empty_output><jupyter_text>Generate Tiles<jupyter_code>import operator
from spatialpower.tissue_generation import assign_labels, random_circle_packing
values, counts = np.unique(tile_types_needed, return_counts=True)
counts<jupyter_output><empty_output><jupyter_text>Heuristic<jupyter_code>def generate_tiles_heuristic(adjacency_matrix, C, graph, p, H, reg_code, outdir, n_tiles):
    
    #Set up positions
    position_dict = dict()
    for i in range(0, C.shape[0]):
        position_dict[i] = C[i, :]
       
    i=0
    while i <= n_tiles:
        attribute_dict = assign_labels.heuristic_assignment(graph, p, H, 'region', 300, position_dict, 100)
        heuristic_B = build_assignment_matrix(attribute_dict, n_cell_types)
        np.save(outdir + str(reg_code) + '_tile' + str(i) + '_' + str(C.shape[0]) + 'cells.npy', heuristic_B)
        if i % 50 == 0: 
            print(i)
        i += 1
    
    return
    
adjacency_matrix = np.load('../data/A_28cells.npy')
C = np.load('../data/C_28cells.npy')
tile_count_dict = dict(zip(values, counts))
outdir = '../data/tiles/'
graph = nx.from_numpy_matrix(adjacency_matrix)
for j in range(0, len(values) - 1):
    reg_name = values[j]
    n_tiles = tile_count_dict[reg_name]
    print(reg_name)
    print("Generating %s tiles..." % n_tiles)
    outdir = '../data/tiles/'

    reg_code = reg_name.replace(' ', '-')


    p = np.load('../data/' + reg_code + '_p_'+ str(search_radius) + 'px.npy')
    H = np.load('../data/' + reg_code + '_H_'+ str(search_radius) + 'px.npy')

    generate_tiles_heuristic(adjacency_matrix, C, graph, p, H, reg_code, outdir, n_tiles)<jupyter_output>Hippocampus
Generating 20 tiles
0
Internal Capsule Caudoputamen
Generating 6 tiles
0
Layer 2-3 lateral
Generating 10 tiles
0
Layer 2-3 medial
Generating 18 tiles
0
Layer 3-4
Generating 3 tiles
0
Layer 4
Generating 27 tiles
0
Layer 5
Generating 11 tiles
0
Layer 6
Generating 31 tiles
0
Pia Layer 1
Generating 12 tiles
0
Ventricle
Generating 5 tiles
0
White matter
Generating 39 tiles
0<jupyter_text>Stitching tiles   Composite C<jupyter_code>import math 
tile_size = 300 #this is the size of the GENERATED TILE!
n_cells_per_tile = C.shape[0]

y_bounds = np.linspace(0, tile_size*w, num=w + 1)
x_bounds = np.linspace(0, tile_size*l, num=l + 1)

shifts = []
C_composite = C.copy()
for i in range(0, len(tile_types_needed)):
    
    if i > 0:
        horiz_pos = math.floor((i)/w) #returns a 0 indexed position in the row (x adjustment in composite)
        vert_pos = i % w #returns a 0 indexed position in the column (e.g. y adjustment in composite)
        #tile_type = tile_ids[i]
        x_inc = x_bounds[1]
        y_inc = y_bounds[1]

        x_adjustment = horiz_pos * x_inc
        y_adjustment = vert_pos * y_inc

        shifts.append([x_adjustment, y_adjustment])

        C_shift = np.zeros(C.shape)
        C_shift[:, 0] = C[:, 0] + x_adjustment + np.random.rand()
        C_shift[:, 1] = C[:, 1] + y_adjustment + np.random.rand()
        
        C_composite = np.vstack((C_composite, C_shift))

#Remove the elements of C that are in blank tiles. 
a, = np.where(np.array(tile_types_needed) == 'null')

C_composite_trim = C_composite.copy()
to_delete = []
for tile_id in a:
    temp = [i + n_cells_per_tile*tile_id for i in range(0, n_cells_per_tile)]
    for t in temp:
        to_delete.append(t)
        
mask = [i for i in range(0, C_composite.shape[0]) if i not in to_delete]

C_composite_trim = C_composite[mask]
np.save('../data/tiles/C_composite_trim.npy', C_composite_trim)<jupyter_output><empty_output><jupyter_text>Composite B<jupyter_code>from glob import glob
B_lists = []
for i in range(0, len(values) - 1):
    reg_name = values[i] 
    reg_code = reg_name.replace(' ', '-')    
    
    B_list = glob('../data/tiles/'+ reg_code + '_*.npy')
    B_lists.append(B_list) 

B_dict = dict(zip(values[:-1],B_lists))
indices = dict(zip(values, [0 for i in range(0, len(values))]))
for i in range(0, len(tile_types_needed)):
    if i%100 == 0:
        print(i)
    tile_type = tile_types_needed[i]
    
    if tile_type != 'null':
        files = B_dict[tile_type]
        arr = files[indices[tile_type]]
        indices[tile_type] += 1
    
        if i == 0:
            B_composite = np.load(arr)
        else:
            try:
                B_composite = np.vstack((B_composite, np.load(arr)))
            except NameError:
                B_composite = np.load(arr)
            if np.load(arr).shape[0] != n_cells_per_tile:
                print(arr)

np.save('../data/tiles/B_composite.npy', B_composite)<jupyter_output>0
100
200<jupyter_text>Composite Graph<jupyter_code>adjacency_matrix = np.load('../data/A_28cells.npy')
C = np.load('../data/C_28cells.npy')
#R = np.load('../../R_51_cell_tissue_scaffold_tile.npy')
graph = nx.from_numpy_matrix(adjacency_matrix)
max_x = max(C[:,0])
max_y = max(C[:,1])
buffer = 30 #how close to the border we want to consider
x_edge_donors, = np.where(C[:, 0] > (max_x - buffer)) #these are the nodes close to the right side that need to be connected to
x_edge_acceptors, = np.where(C[:, 0] < buffer) #these are the nodes close to the left side that need to be connected to
a = np.hstack((x_edge_acceptors.reshape((x_edge_acceptors.shape[0],1)), C[x_edge_acceptors, :]))
a[a[:,2].argsort()] #sort by y position
b = a[a[:,2].argsort()]
sorted_x_edge_acceptors = b[:,0]

a = np.hstack((x_edge_donors.reshape((x_edge_donors.shape[0],1)), C[x_edge_donors, :]))
a[a[:,2].argsort()] #sort by y position
b = a[a[:,2].argsort()]
sorted_x_edge_donors = b[:,0]

y_edge_donors, = np.where(C[:, 1] > (max_y - buffer)) #these are the nodes close to the bottom side that need to be connected to
y_edge_acceptors, = np.where(C[:, 1] < buffer) #these are the nodes close to the top side that need to be connected to

a = np.hstack((y_edge_acceptors.reshape((y_edge_acceptors.shape[0],1)), C[y_edge_acceptors, :]))
a[a[:,1].argsort()] #sort by y position
b = a[a[:,1].argsort()]
sorted_y_edge_acceptors = b[:,0]

a = np.hstack((y_edge_donors.reshape((y_edge_donors.shape[0],1)), C[y_edge_donors, :]))
a[a[:,1].argsort()] #sort by y position
b = a[a[:,1].argsort()]
sorted_y_edge_donors = b[:,0]
x_stitch_dict = dict()
y_stitch_dict = dict()

for i in range(0, sorted_x_edge_donors.shape[0]):
    if i < len(sorted_x_edge_acceptors):
        donor_id = int(sorted_x_edge_donors[i])
        acceptor_id = int(sorted_x_edge_acceptors[i])
    else:
        #the donors list longer than acceptors
        donor_id = int(sorted_x_edge_donors[i])
        acceptor_id = int(sorted_x_edge_acceptors[-1]) #just stick it to the last node
    
    x_stitch_dict[donor_id] = acceptor_id
    
for i in range(0, sorted_y_edge_donors.shape[0]):
    if i < len(sorted_y_edge_acceptors):
        donor_id = int(sorted_y_edge_donors[i])
        acceptor_id = int(sorted_y_edge_acceptors[i])
    else:
        #the donors list longer than acceptors
        donor_id = int(sorted_y_edge_donors[i])
        acceptor_id = int(sorted_y_edge_acceptors[-1]) #just stick it to the last node
    
    y_stitch_dict[donor_id] = acceptor_id
new_graph = graph.copy()
composite_graph = nx.disjoint_union(graph, new_graph)

for i in range(0, len(tile_types_needed) - 2):
    #this can be quite slow for large n_tiles, be sure to save your result.
    #TO DO: implement alternative approach for speed. 
    if i % 100 == 0:
        print(i)
    composite_graph = nx.disjoint_union(composite_graph, new_graph) 
n_cells_per_tile = C.shape[0]
current_tile_id = 0
l=int(l)
w=int(w)
for tile_x_index in range(0, l):
    for tile_y_index in range(0,w):
        if tile_x_index < l - 1:
            if tile_y_index < w-1: 
                for i in sorted_y_edge_donors:
                    y_acceptor = y_stitch_dict[i]
                    source = int(i) + (n_cells_per_tile * current_tile_id)
                    target = y_acceptor + (n_cells_per_tile * (current_tile_id + 1))
                    composite_graph.add_edge(source, target)

                for i in sorted_x_edge_donors:
                    x_acceptor = x_stitch_dict[i]
                    source = int(i) + (n_cells_per_tile * current_tile_id)
                    #target = x_acceptor + (239 * (current_tile_id + 20))
                    target = (x_acceptor + (n_cells_per_tile * current_tile_id)) + (n_cells_per_tile * w)
                    composite_graph.add_edge(source, target)
            else:
                # we're in the bottom row, so only stitch horizontally.
                for i in sorted_x_edge_donors:
                    x_acceptor = x_stitch_dict[i]
                    source = int(i) + (n_cells_per_tile * current_tile_id)
                    target = (x_acceptor + (n_cells_per_tile * current_tile_id)) + (n_cells_per_tile * w)
                    composite_graph.add_edge(source, target)
                
        else:
            #we're in the last column, so only stitch vertically.
            if tile_y_index < w-1: 
                for i in sorted_y_edge_donors:
                    y_acceptor = y_stitch_dict[i]
                    source = int(i) + (n_cells_per_tile * current_tile_id)
                    target = y_acceptor + (n_cells_per_tile * (current_tile_id + 1))
                    composite_graph.add_edge(source, target)
            else:
                #this is the bottom right corner...everything is stitched!
                print("Done!")
           
        current_tile_id += 1
#sorted_y_edge_acceptors.astype(int) + (239 * tile_y_index)


#Remove the nodes of A that are in blank tiles. 
a, = np.where(np.array(tile_types_needed) == 'null')

for tile_id in a:
    #Get the nodes to delete:
    node_ids = [i + n_cells_per_tile*tile_id for i in range(0, n_cells_per_tile)]
    
    for n in node_ids:
        composite_graph.remove_node(n)
A_composite = nx.to_scipy_sparse_matrix(composite_graph)
sparse.save_npz('../data/tiles/A_composite.npz', A_composite)<jupyter_output><empty_output><jupyter_text>Visualize IST<jupyter_code>B_composite = np.load('../data/tiles/B_composite.npy')
C_composite_trim = np.load('../data/tiles/C_composite_trim.npy')
A_composite = sparse.load_npz('../data/tiles/A_composite.npz')
type_dict = dict()
for i in range(0, B_composite.shape[0]):

    for j in range(0, B_composite.shape[1]):
        if B_composite[i, j] == 1:
            type_dict[i] = j
            
position_dict = dict()
for i in range(0, C_composite_trim.shape[0]):
    position_dict[i] = C_composite_trim[i, :]
point_list = [position_dict[i] for i in range(0, C_composite_trim.shape[0])]
colors = [type_color_dict[type_dict[i]] for i in range(0, B_composite.shape[0])]
plt.clf()
fig, ax2 = plt.subplots(1,1,figsize=(6,6))
plt.scatter(np.array(point_list)[:,0], np.array(point_list)[:,1], c=colors, s=1)
#plt.hlines(y_bounds,min(x_bounds), max(x_bounds))
#plt.vlines(x_bounds,min(y_bounds), max(y_bounds))
#ax2.set(xlim=(0 - 0.01*ab[0], ab[0] + 0.01 * ab[0]), ylim=(7100, 0 - 0.01*ab[1]/2))
#plt.savefig('../fig/full_osmfish_brain_400px_300tiles_28cells.png', dpi=400)
plt.gca().invert_yaxis()
plt.show()
plt.clf()
fix, (ax1, ax2) = plt.subplots(1,2, figsize = (8,4))
ax1.scatter(df['X'], df['Y'], color=df['cell_type_color'], marker='o', s=1)
ax1.set_title('real')
ax1.set_ylim(50000,0)
ax2.scatter(np.array(point_list)[:,0], np.array(point_list)[:,1], c=colors, s=1)
ax2.set_title('IST')
ax2.set_ylim(8000,0)
#plt.gca().invert_yaxis()
plt.savefig('../fig/full_osmFISH_data_real_IST_sidebyside.pdf')
plt.tight_layout()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1431693480889054, 'fraction_numerical': 0.02157537431414489, 'mean_word_length': 3.6551948051948053, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '98727', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 8595, 'n_tokens_neox': 8218, 'n_words': 1680, 'path': 'osmfish_cortex/osmfish_generation.ipynb', 'repo_name': 'klarman-cell-observatory/PowerAnalysisForSpatialOmics', 'size': '21506'}