ModDotPlot 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1264 @@
1
+ from plotnine import (
2
+ ggsave,
3
+ ggplot,
4
+ aes,
5
+ geom_histogram,
6
+ scale_color_discrete,
7
+ element_blank,
8
+ theme,
9
+ xlab,
10
+ scale_fill_manual,
11
+ scale_color_cmap,
12
+ coord_cartesian,
13
+ ylab,
14
+ scale_x_continuous,
15
+ scale_y_continuous,
16
+ geom_tile,
17
+ coord_fixed,
18
+ facet_grid,
19
+ labs,
20
+ element_line,
21
+ element_text,
22
+ theme_light,
23
+ theme_void,
24
+ geom_blank,
25
+ annotate,
26
+ element_rect,
27
+ coord_flip,
28
+ theme_minimal,
29
+ )
30
+ import pandas as pd
31
+ import numpy as np
32
+ from PIL import Image
33
+ import patchworklib as pw
34
+ import math
35
+ import os
36
+ import sys
37
+ from moddotplot.parse_fasta import printProgressBar
38
+
39
+ from moddotplot.const import (
40
+ DIVERGING_PALETTES,
41
+ QUALITATIVE_PALETTES,
42
+ SEQUENTIAL_PALETTES,
43
+ )
44
+ from typing import List
45
+ from palettable.colorbrewer import qualitative, sequential, diverging
46
+
47
+
48
+ def is_plot_empty(p):
49
+ # Check if the plot has data or any layers
50
+ return len(p.layers) == 0 and p.data.empty
51
+
52
+
53
+ def check_pascal(single_val, double_val):
54
+ try:
55
+ if len(single_val) == 2:
56
+ assert len(double_val) == 1
57
+ elif len(single_val) == 3:
58
+ assert len(double_val) == 3
59
+ elif len(single_val) == 4:
60
+ assert len(double_val) == 6
61
+ elif len(single_val) == 5:
62
+ assert len(double_val) == 10
63
+ elif len(single_val) == 6:
64
+ assert len(double_val) == 15
65
+ elif len(single_val) == 0:
66
+ assert len(double_val) == (1 or 3 or 6 or 10 or 15)
67
+ except AssertionError as e:
68
+ print(
69
+ f"Missing bed files required to create grid. Please verify all bed files are included."
70
+ )
71
+ sys.exit(8)
72
+
73
+
74
+ def reverse_pascal(double_vals):
75
+ if len(double_vals) == 1:
76
+ return 2
77
+ elif len(double_vals) == 3:
78
+ return 3
79
+ elif len(double_vals) == 6:
80
+ return 4
81
+ elif len(double_vals) == 10:
82
+ return 5
83
+ elif len(double_vals) == 15:
84
+ return 6
85
+ else:
86
+ sys.exit(9)
87
+
88
+
89
+ # Hardcoding for now, I have the formula just lazy
90
+ def transpose_order(double_vals):
91
+ if len(double_vals) == 1:
92
+ return [0]
93
+ elif len(double_vals) == 3:
94
+ return [0, 1, 2]
95
+ elif len(double_vals) == 6:
96
+ return [0, 1, 3, 2, 4, 5]
97
+ elif len(double_vals) == 10:
98
+ return [0, 1, 4, 2, 5, 7, 3, 6, 8, 9]
99
+ elif len(double_vals) == 15:
100
+ return [0, 1, 5, 2, 6, 9, 3, 7, 10, 12, 4, 8, 11, 13, 14]
101
+
102
+
103
+ def check_st_en_equality(df):
104
+ unequal_rows = df[(df["q_st"] != df["r_st"]) | (df["q_en"] != df["r_en"])]
105
+ unequal_rows.loc[:, ["q_en", "r_en", "q_st", "r_st"]] = unequal_rows[
106
+ ["r_en", "q_en", "r_st", "q_st"]
107
+ ].values
108
+
109
+ df = pd.concat([df, unequal_rows], ignore_index=True)
110
+
111
+ return df
112
+
113
+
114
+ def make_k(vals):
115
+ return [number / 1000 for number in vals]
116
+
117
+
118
+ def make_m(vals):
119
+ return [number / 1e6 for number in vals]
120
+
121
+
122
+ def make_g(vals):
123
+ return [number / 1e9 for number in vals]
124
+
125
+
126
+ def make_scale(vals: list) -> list:
127
+ scaled = [number for number in vals]
128
+ if scaled[-1] < 100000:
129
+ return make_k(scaled)
130
+ elif scaled[-1] > 100000000:
131
+ return make_g(scaled)
132
+ else:
133
+ return make_m(scaled)
134
+
135
+
136
+ def overlap_axis(rotated_plot, filename, prefix):
137
+ scale_factor = math.sqrt(2) + 0.04
138
+ new_width = int(rotated_plot.width / scale_factor)
139
+ new_height = int(rotated_plot.height / scale_factor)
140
+ resized_rotated_plot = rotated_plot.resize((new_width, new_height), Image.LANCZOS)
141
+
142
+ # Step 3: Overlay the resized rotated heatmap onto the original axes
143
+
144
+ # Open the original heatmap with axes
145
+ image_with_axes = Image.open(filename)
146
+
147
+ # Create a blank image with the same size as the original
148
+ final_image = Image.new("RGBA", image_with_axes.size)
149
+
150
+ # Calculate the position to center the resized rotated image within the original plot area
151
+ x_offset = (final_image.width - resized_rotated_plot.width) // 2
152
+ y_offset = (final_image.height - resized_rotated_plot.height) // 2
153
+ y_offset += 2400
154
+ x_offset += 30
155
+
156
+ # Paste the original image with axes onto the final image
157
+ final_image.paste(image_with_axes, (0, 0))
158
+
159
+ # Paste the resized rotated plot onto the final image
160
+ final_image.paste(resized_rotated_plot, (x_offset, y_offset), resized_rotated_plot)
161
+ width, height = final_image.size
162
+ cropped_image = final_image.crop((0, height // 2.6, width, height))
163
+
164
+ # Save or show the final image
165
+ cropped_image.save(f"{prefix}_TRI.png")
166
+ cropped_image.save(f"{prefix}_TRI.pdf", "PDF", resolution=100.0)
167
+
168
+ # Remove temp files
169
+ if os.path.exists(filename):
170
+ os.remove(filename)
171
+
172
+
173
+ def get_colors(sdf, ncolors, is_freq, custom_breakpoints):
174
+ assert ncolors > 2 and ncolors < 12
175
+ try:
176
+ bot = math.floor(min(sdf["perID_by_events"]))
177
+ except ValueError:
178
+ bot = 0
179
+ top = 100.0
180
+ interval = (top - bot) / ncolors
181
+ breaks = []
182
+ if is_freq:
183
+ breaks = np.unique(
184
+ np.quantile(sdf["perID_by_events"], np.arange(0, 1.01, 1 / ncolors))
185
+ )
186
+ else:
187
+ breaks = [bot + i * interval for i in range(ncolors + 1)]
188
+ if custom_breakpoints:
189
+ breaks = np.asfarray(custom_breakpoints)
190
+ labels = np.arange(len(breaks) - 1)
191
+ # corner case of only one %id value
192
+ if len(breaks) == 1:
193
+ return pd.factorize([1] * len(sdf["perID_by_events"]))[0]
194
+ else:
195
+ tmp = pd.cut(
196
+ sdf["perID_by_events"], bins=breaks, labels=labels, include_lowest=True
197
+ )
198
+ return tmp
199
+
200
+
201
+ # TODO: Remove pandas dependency
202
+ def read_df_from_file(file_path):
203
+ data = pd.read_csv(file_path, delimiter="\t")
204
+ return data
205
+
206
+
207
+ def read_df(
208
+ pj,
209
+ palette,
210
+ palette_orientation,
211
+ is_freq,
212
+ custom_colors,
213
+ custom_breakpoints,
214
+ from_file,
215
+ ):
216
+ df = ""
217
+ if from_file is not None:
218
+ df = from_file
219
+ else:
220
+ data = pj[0]
221
+ df = pd.DataFrame(data[1:], columns=data[0])
222
+ hexcodes = []
223
+ new_hexcodes = []
224
+ if palette in DIVERGING_PALETTES:
225
+ function_name = getattr(diverging, palette)
226
+ hexcodes = function_name.hex_colors
227
+ if palette_orientation == "+":
228
+ palette_orientation = "-"
229
+ else:
230
+ palette_orientation = "+"
231
+ elif palette in QUALITATIVE_PALETTES:
232
+ function_name = getattr(qualitative, palette)
233
+ hexcodes = function_name.hex_colors
234
+ elif palette in SEQUENTIAL_PALETTES:
235
+ function_name = getattr(sequential, palette)
236
+ hexcodes = function_name.hex_colors
237
+ else:
238
+ function_name = getattr(sequential, "Spectral_11")
239
+ palette_orientation = "-"
240
+ hexcodes = function_name.hex_colors
241
+
242
+ if palette_orientation == "-":
243
+ new_hexcodes = hexcodes[::-1]
244
+ else:
245
+ new_hexcodes = hexcodes
246
+
247
+ if custom_colors:
248
+ new_hexcodes = custom_colors
249
+
250
+ ncolors = len(new_hexcodes)
251
+ # Get colors for each row based on the values in the dataframe
252
+ df["discrete"] = get_colors(df, ncolors, is_freq, custom_breakpoints)
253
+ # Rename columns if they have different names in the dataframe
254
+ if "query_name" in df.columns or "#query_name" in df.columns:
255
+ df.rename(
256
+ columns={
257
+ "#query_name": "q",
258
+ "query_start": "q_st",
259
+ "query_end": "q_en",
260
+ "reference_name": "r",
261
+ "reference_start": "r_st",
262
+ "reference_end": "r_en",
263
+ },
264
+ inplace=True,
265
+ )
266
+
267
+ # Calculate the window size
268
+ try:
269
+ window = max(df["q_en"] - df["q_st"])
270
+ except ValueError:
271
+ window = 0
272
+
273
+ # Calculate the position of the first and second intervals
274
+ df["first_pos"] = df["q_st"] / window
275
+ df["second_pos"] = df["r_st"] / window
276
+
277
+ return df
278
+
279
+
280
+ def make_dot(sdf, title_name, palette, palette_orientation, colors, breaks, xlim):
281
+ if not breaks:
282
+ breaks = True
283
+ else:
284
+ breaks = [float(number) for number in breaks]
285
+ if not xlim:
286
+ xlim = 0
287
+ hexcodes = []
288
+ new_hexcodes = []
289
+ if palette in DIVERGING_PALETTES:
290
+ function_name = getattr(diverging, palette)
291
+ hexcodes = function_name.hex_colors
292
+ if palette_orientation == "+":
293
+ palette_orientation = "-"
294
+ else:
295
+ palette_orientation = "+"
296
+ elif palette in QUALITATIVE_PALETTES:
297
+ function_name = getattr(qualitative, palette)
298
+ hexcodes = function_name.hex_colors
299
+ elif palette in SEQUENTIAL_PALETTES:
300
+ function_name = getattr(sequential, palette)
301
+ hexcodes = function_name.hex_colors
302
+ else:
303
+ function_name = getattr(sequential, "Spectral_11")
304
+ palette_orientation = "-"
305
+ hexcodes = function_name.hex_colors
306
+
307
+ if palette_orientation == "-":
308
+ new_hexcodes = hexcodes[::-1]
309
+ else:
310
+ new_hexcodes = hexcodes
311
+ if colors:
312
+ new_hexcodes = colors
313
+ max_val = max(sdf["q_en"].max(), sdf["r_en"].max(), xlim)
314
+ try:
315
+ window = max(sdf["q_en"] - sdf["q_st"])
316
+ except:
317
+ p = (
318
+ ggplot(aes(x=[], y=[]))
319
+ + theme_minimal() # Use a minimal theme with gridlines
320
+ + theme(
321
+ panel_grid_major=element_blank(), # Remove major gridlines (optional)
322
+ panel_grid_minor=element_blank(),
323
+ ) # Remove minor gridlines (optional)
324
+ )
325
+ return p
326
+ if max_val < 100000:
327
+ x_label = "Genomic Position (Kbp)"
328
+ elif max_val < 100000000:
329
+ x_label = "Genomic Position (Mbp)"
330
+ else:
331
+ x_label = "Genomic Position (Gbp)"
332
+ p = (
333
+ ggplot(sdf)
334
+ + geom_tile(
335
+ aes(x="q_st", y="r_st", fill="discrete", height=window, width=window)
336
+ )
337
+ + scale_color_discrete(guide=False)
338
+ + scale_fill_manual(
339
+ values=new_hexcodes,
340
+ guide=False,
341
+ )
342
+ + theme(
343
+ legend_position="none",
344
+ panel_grid_major=element_blank(),
345
+ panel_grid_minor=element_blank(),
346
+ plot_background=element_blank(),
347
+ panel_background=element_blank(),
348
+ axis_line=element_line(color="black"), # Adjust axis line size
349
+ axis_text=element_text(
350
+ family=["Dejavu Sans"]
351
+ ), # Change axis text font and size
352
+ axis_ticks_major=element_line(),
353
+ title=element_text(
354
+ family=["Dejavu Sans"], # Change title font family
355
+ ),
356
+ )
357
+ + scale_x_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
358
+ + scale_y_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
359
+ + coord_fixed(ratio=1)
360
+ + facet_grid("r ~ q")
361
+ + labs(x="", y="", title=title_name)
362
+ )
363
+
364
+ # Adjust x-axis label size
365
+ # p += theme(axis_title_x=element_text())
366
+
367
+ return p
368
+
369
+
370
+ def make_dot2(sdf, title_name, palette, palette_orientation, colors, breaks, xlim):
371
+ if not breaks:
372
+ breaks = True
373
+ else:
374
+ breaks = [float(number) for number in breaks]
375
+ if not xlim:
376
+ xlim = 0
377
+ hexcodes = []
378
+ new_hexcodes = []
379
+ if palette in DIVERGING_PALETTES:
380
+ function_name = getattr(diverging, palette)
381
+ hexcodes = function_name.hex_colors
382
+ if palette_orientation == "+":
383
+ palette_orientation = "-"
384
+ else:
385
+ palette_orientation = "+"
386
+ elif palette in QUALITATIVE_PALETTES:
387
+ function_name = getattr(qualitative, palette)
388
+ hexcodes = function_name.hex_colors
389
+ elif palette in SEQUENTIAL_PALETTES:
390
+ function_name = getattr(sequential, palette)
391
+ hexcodes = function_name.hex_colors
392
+ else:
393
+ function_name = getattr(sequential, "Spectral_11")
394
+ palette_orientation = "-"
395
+ hexcodes = function_name.hex_colors
396
+
397
+ if palette_orientation == "-":
398
+ new_hexcodes = hexcodes[::-1]
399
+ else:
400
+ new_hexcodes = hexcodes
401
+ if colors:
402
+ new_hexcodes = colors
403
+ max_val = max(sdf["q_en"].max(), sdf["r_en"].max(), xlim)
404
+ try:
405
+ window = max(sdf["q_en"] - sdf["q_st"])
406
+ except:
407
+ p = (
408
+ ggplot(aes(x=[], y=[]))
409
+ + theme_minimal() # Use a minimal theme with gridlines
410
+ + theme(
411
+ panel_grid_major=element_blank(), # Remove major gridlines (optional)
412
+ panel_grid_minor=element_blank(),
413
+ ) # Remove minor gridlines (optional)
414
+ )
415
+ return p
416
+ if max_val < 100000:
417
+ x_label = "Genomic Position (Kbp)"
418
+ elif max_val < 100000000:
419
+ x_label = "Genomic Position (Mbp)"
420
+ else:
421
+ x_label = "Genomic Position (Gbp)"
422
+ p = (
423
+ ggplot(sdf)
424
+ + geom_tile(
425
+ aes(x="q_st", y="r_st", fill="discrete", height=window, width=window)
426
+ )
427
+ + scale_color_discrete(guide=False)
428
+ + scale_fill_manual(
429
+ values=new_hexcodes,
430
+ guide=False,
431
+ )
432
+ + theme(
433
+ legend_position="none",
434
+ panel_grid_major=element_blank(),
435
+ panel_grid_minor=element_blank(),
436
+ plot_background=element_blank(),
437
+ panel_background=element_blank(),
438
+ axis_line=element_line(color="black"), # Adjust axis line size
439
+ axis_text=element_text(
440
+ family=["Dejavu Sans"]
441
+ ), # Change axis text font and size
442
+ axis_ticks_major=element_line(),
443
+ title=element_text(
444
+ family=["Dejavu Sans"], # Change title font family
445
+ ),
446
+ )
447
+ + scale_x_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
448
+ + scale_y_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
449
+ + coord_fixed(ratio=1)
450
+ + labs(x=None, y=None, title=title_name)
451
+ )
452
+
453
+ # Adjust x-axis label size
454
+ p += theme(axis_title_x=element_blank())
455
+ p += theme(axis_title_y=element_blank())
456
+ # p += theme(title=element_blank())
457
+
458
+ return p
459
+
460
+
461
+ def make_dot3(sdf, title_name, palette, palette_orientation, colors, breaks, xlim):
462
+ # sdf = sdf.transpose()
463
+ if not breaks:
464
+ breaks = True
465
+ else:
466
+ breaks = [float(number) for number in breaks]
467
+ if not xlim:
468
+ xlim = 0
469
+ hexcodes = []
470
+ new_hexcodes = []
471
+ if palette in DIVERGING_PALETTES:
472
+ function_name = getattr(diverging, palette)
473
+ hexcodes = function_name.hex_colors
474
+ if palette_orientation == "+":
475
+ palette_orientation = "-"
476
+ else:
477
+ palette_orientation = "+"
478
+ elif palette in QUALITATIVE_PALETTES:
479
+ function_name = getattr(qualitative, palette)
480
+ hexcodes = function_name.hex_colors
481
+ elif palette in SEQUENTIAL_PALETTES:
482
+ function_name = getattr(sequential, palette)
483
+ hexcodes = function_name.hex_colors
484
+ else:
485
+ function_name = getattr(sequential, "Spectral_11")
486
+ palette_orientation = "-"
487
+ hexcodes = function_name.hex_colors
488
+
489
+ if palette_orientation == "-":
490
+ new_hexcodes = hexcodes[::-1]
491
+ else:
492
+ new_hexcodes = hexcodes
493
+ if colors:
494
+ new_hexcodes = colors
495
+ max_val = max(sdf["q_en"].max(), sdf["r_en"].max(), xlim)
496
+ try:
497
+ window = max(sdf["q_en"] - sdf["q_st"])
498
+ except:
499
+ p = (
500
+ ggplot(aes(x=[], y=[]))
501
+ + theme_minimal() # Use a minimal theme with gridlines
502
+ + theme(
503
+ panel_grid_major=element_blank(), # Remove major gridlines (optional)
504
+ panel_grid_minor=element_blank(),
505
+ ) # Remove minor gridlines (optional)
506
+ )
507
+ return p
508
+ if max_val < 100000:
509
+ x_label = "Genomic Position (Kbp)"
510
+ elif max_val < 100000000:
511
+ x_label = "Genomic Position (Mbp)"
512
+ else:
513
+ x_label = "Genomic Position (Gbp)"
514
+ p = (
515
+ ggplot(sdf)
516
+ + geom_tile(
517
+ aes(x="r_st", y="q_st", fill="discrete", height=window, width=window)
518
+ )
519
+ + scale_color_discrete(guide=False)
520
+ + scale_fill_manual(
521
+ values=new_hexcodes,
522
+ guide=False,
523
+ )
524
+ + theme(
525
+ legend_position="none",
526
+ panel_grid_major=element_blank(),
527
+ panel_grid_minor=element_blank(),
528
+ plot_background=element_blank(),
529
+ panel_background=element_blank(),
530
+ axis_line=element_line(color="black"), # Adjust axis line size
531
+ axis_text=element_text(
532
+ family=["Dejavu Sans"]
533
+ ), # Change axis text font and size
534
+ axis_ticks_major=element_line(),
535
+ title=element_text(
536
+ family=["Dejavu Sans"], # Change title font family
537
+ ),
538
+ )
539
+ + scale_x_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
540
+ + scale_y_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
541
+ + coord_fixed(ratio=1)
542
+ + labs(x=None, y=None, title=title_name)
543
+ )
544
+
545
+ # Adjust x-axis label size
546
+ p += theme(axis_title_x=element_blank())
547
+ p += theme(axis_title_y=element_blank())
548
+ # p += theme(title=element_blank())
549
+
550
+ return p
551
+
552
+
553
+ def make_tri(sdf, title_name, palette, palette_orientation, colors, breaks, xlim):
554
+ if not breaks:
555
+ breaks = True
556
+ else:
557
+ breaks = [float(number) for number in breaks]
558
+ if not xlim:
559
+ xlim = 0
560
+ hexcodes = []
561
+ new_hexcodes = []
562
+ if palette in DIVERGING_PALETTES:
563
+ function_name = getattr(diverging, palette)
564
+ hexcodes = function_name.hex_colors
565
+ if palette_orientation == "+":
566
+ palette_orientation = "-"
567
+ else:
568
+ palette_orientation = "+"
569
+ elif palette in QUALITATIVE_PALETTES:
570
+ function_name = getattr(qualitative, palette)
571
+ hexcodes = function_name.hex_colors
572
+ elif palette in SEQUENTIAL_PALETTES:
573
+ function_name = getattr(sequential, palette)
574
+ hexcodes = function_name.hex_colors
575
+ else:
576
+ function_name = getattr(sequential, "Spectral_11")
577
+ palette_orientation = "-"
578
+ hexcodes = function_name.hex_colors
579
+
580
+ if palette_orientation == "-":
581
+ new_hexcodes = hexcodes[::-1]
582
+ else:
583
+ new_hexcodes = hexcodes
584
+ if colors:
585
+ new_hexcodes = colors
586
+ max_val = max(sdf["q_en"].max(), sdf["r_en"].max(), xlim)
587
+ window = max(sdf["q_en"] - sdf["q_st"])
588
+ if max_val < 100000:
589
+ x_label = "Genomic Position (Kbp)"
590
+ elif max_val < 100000000:
591
+ x_label = "Genomic Position (Mbp)"
592
+ else:
593
+ x_label = "Genomic Position (Gbp)"
594
+ p = (
595
+ ggplot(sdf)
596
+ + geom_tile(
597
+ aes(x="q_st", y="r_st", fill="discrete", height=window, width=window)
598
+ )
599
+ + scale_color_discrete(guide=False)
600
+ + scale_fill_manual(
601
+ values=new_hexcodes,
602
+ guide=False,
603
+ )
604
+ + theme(
605
+ legend_position="none",
606
+ panel_grid_major=element_blank(),
607
+ panel_grid_minor=element_blank(),
608
+ plot_background=element_blank(),
609
+ panel_background=element_blank(),
610
+ axis_line=element_line(color="black"), # Adjust axis line size
611
+ axis_text=element_text(
612
+ family=["DejaVu Sans"]
613
+ ), # Change axis text font and size
614
+ axis_ticks_major=element_line(),
615
+ title=element_text(
616
+ family=["DejaVu Sans"], # Change title font family
617
+ ),
618
+ )
619
+ + scale_x_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
620
+ + scale_y_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
621
+ + coord_fixed(ratio=1)
622
+ + labs(x="", y="", title=title_name)
623
+ )
624
+
625
+ # Adjust x-axis label size
626
+ p += theme(axis_title_x=element_text())
627
+
628
+ return p
629
+
630
+
631
+ def make_tri2(sdf, title_name, palette, palette_orientation, colors, breaks, xlim):
632
+ if not breaks:
633
+ breaks = True
634
+ else:
635
+ breaks = [float(number) for number in breaks]
636
+ if not xlim:
637
+ xlim = 0
638
+ hexcodes = []
639
+ new_hexcodes = []
640
+ if palette in DIVERGING_PALETTES:
641
+ function_name = getattr(diverging, palette)
642
+ hexcodes = function_name.hex_colors
643
+ if palette_orientation == "+":
644
+ palette_orientation = "-"
645
+ else:
646
+ palette_orientation = "+"
647
+ elif palette in QUALITATIVE_PALETTES:
648
+ function_name = getattr(qualitative, palette)
649
+ hexcodes = function_name.hex_colors
650
+ elif palette in SEQUENTIAL_PALETTES:
651
+ function_name = getattr(sequential, palette)
652
+ hexcodes = function_name.hex_colors
653
+ else:
654
+ function_name = getattr(sequential, "Spectral_11")
655
+ palette_orientation = "-"
656
+ hexcodes = function_name.hex_colors
657
+
658
+ if palette_orientation == "-":
659
+ new_hexcodes = hexcodes[::-1]
660
+ else:
661
+ new_hexcodes = hexcodes
662
+ if colors:
663
+ new_hexcodes = colors
664
+ max_val = max(sdf["q_en"].max(), sdf["r_en"].max(), xlim)
665
+ window = max(sdf["q_en"] - sdf["q_st"])
666
+ if max_val < 100000:
667
+ x_label = "Genomic Position (Kbp)"
668
+ elif max_val < 100000000:
669
+ x_label = "Genomic Position (Mbp)"
670
+ else:
671
+ x_label = "Genomic Position (Gbp)"
672
+ p = (
673
+ ggplot(sdf)
674
+ + geom_tile(
675
+ aes(x="q_st", y="r_st", fill="discrete", height=window, width=window),
676
+ alpha=0,
677
+ )
678
+ + scale_color_discrete(guide=False)
679
+ + scale_fill_manual(
680
+ values=new_hexcodes,
681
+ guide=False,
682
+ )
683
+ + theme(
684
+ legend_position="none",
685
+ panel_grid_major=element_blank(),
686
+ panel_grid_minor=element_blank(),
687
+ plot_background=element_blank(),
688
+ panel_background=element_blank(),
689
+ axis_line=element_line(color="black"), # Adjust axis line size
690
+ axis_text=element_text(
691
+ family=["DejaVu Sans"]
692
+ ), # Change axis text font and size
693
+ axis_ticks_major=element_line(),
694
+ axis_line_x=element_line(), # Keep the x-axis line
695
+ axis_line_y=element_blank(), # Remove the y-axis line
696
+ axis_ticks_major_x=element_line(), # Keep x-axis ticks
697
+ axis_ticks_major_y=element_blank(), # Remove y-axis ticks
698
+ axis_text_x=element_line(), # Keep x-axis text
699
+ axis_text_y=element_blank(),
700
+ plot_title=element_blank(),
701
+ )
702
+ + scale_x_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
703
+ + scale_y_continuous(labels=make_scale, limits=[0, max_val], breaks=breaks)
704
+ + coord_fixed(ratio=1)
705
+ + labs(x="", y="", title=title_name)
706
+ )
707
+
708
+ # Adjust x-axis label size
709
+ p += theme(axis_title_x=element_text())
710
+
711
+ return p
712
+
713
+
714
+ def make_hist(sdf, palette, palette_orientation, custom_colors, custom_breakpoints):
715
+ hexcodes = []
716
+ new_hexcodes = []
717
+ if palette in DIVERGING_PALETTES:
718
+ function_name = getattr(diverging, palette)
719
+ hexcodes = function_name.hex_colors
720
+ if palette_orientation == "+":
721
+ palette_orientation = "-"
722
+ else:
723
+ palette_orientation = "+"
724
+ elif palette in QUALITATIVE_PALETTES:
725
+ function_name = getattr(qualitative, palette)
726
+ hexcodes = function_name.hex_colors
727
+ elif palette in SEQUENTIAL_PALETTES:
728
+ function_name = getattr(sequential, palette)
729
+ hexcodes = function_name.hex_colors
730
+ else:
731
+ function_name = getattr(sequential, "Spectral_11")
732
+ palette_orientation = "-"
733
+ hexcodes = function_name.hex_colors
734
+
735
+ if palette_orientation == "-":
736
+ new_hexcodes = hexcodes[::-1]
737
+ else:
738
+ new_hexcodes = hexcodes
739
+
740
+ if custom_colors:
741
+ new_hexcodes = custom_colors
742
+ try:
743
+ bot = np.quantile(sdf["perID_by_events"], q=0.001)
744
+ except IndexError:
745
+ bot = 0
746
+ count = sdf.shape[0]
747
+ extra = ""
748
+
749
+ if count > 1e6:
750
+ extra = "\n(thousands)"
751
+
752
+ p = (
753
+ ggplot(data=sdf, mapping=aes(x="perID_by_events", fill="discrete"))
754
+ + geom_histogram(bins=300)
755
+ + scale_color_cmap(cmap_name="plasma")
756
+ + scale_fill_manual(new_hexcodes)
757
+ + theme_light()
758
+ + theme(text=element_text(family=["DejaVu Sans"]))
759
+ + theme(legend_position="none")
760
+ + coord_cartesian(xlim=(bot, 100))
761
+ + xlab("% Identity Estimate")
762
+ + ylab("# of Estimates{}".format(extra))
763
+ )
764
+ return p
765
+
766
+
767
+ def create_grid(
768
+ singles,
769
+ doubles,
770
+ directory,
771
+ palette,
772
+ palette_orientation,
773
+ no_hist,
774
+ single_names,
775
+ double_names,
776
+ is_freq,
777
+ xlim,
778
+ custom_colors,
779
+ custom_breakpoints,
780
+ axes_label,
781
+ is_bed,
782
+ ):
783
+ new_index = []
784
+ transpose_index = []
785
+ check_pascal(singles, doubles)
786
+ # Singles can be empty if not selected
787
+ for i in range(len(single_names)):
788
+ for j in range(i + 1, len(single_names)):
789
+ try:
790
+ index = double_names.index([single_names[i], single_names[j]])
791
+ transpose_index.append(0)
792
+ except:
793
+ index = double_names.index([single_names[j], single_names[i]])
794
+ transpose_index.append(1)
795
+
796
+ new_index.append(index)
797
+
798
+ single_list = []
799
+ double_list = []
800
+ single_heatmap_list = []
801
+ normal_heatmap_list = []
802
+ transpose_heatmap_list = []
803
+ for matrix in singles:
804
+ if is_bed:
805
+ df = read_df(
806
+ None,
807
+ palette,
808
+ palette_orientation,
809
+ is_freq,
810
+ custom_colors,
811
+ custom_breakpoints,
812
+ matrix,
813
+ )
814
+ else:
815
+ df = read_df(
816
+ [matrix],
817
+ palette,
818
+ palette_orientation,
819
+ is_freq,
820
+ custom_colors,
821
+ custom_breakpoints,
822
+ None,
823
+ )
824
+ single_list.append(df)
825
+ for matrix in doubles:
826
+ if is_bed:
827
+ df = read_df(
828
+ None,
829
+ palette,
830
+ palette_orientation,
831
+ is_freq,
832
+ custom_colors,
833
+ custom_breakpoints,
834
+ matrix,
835
+ )
836
+ else:
837
+ df = read_df(
838
+ [matrix],
839
+ palette,
840
+ palette_orientation,
841
+ is_freq,
842
+ custom_colors,
843
+ custom_breakpoints,
844
+ None,
845
+ )
846
+ double_list.append(df)
847
+ for plot in single_list:
848
+ heatmap = make_dot2(
849
+ plot,
850
+ plot["q"].iloc[1],
851
+ palette,
852
+ palette_orientation,
853
+ custom_colors,
854
+ axes_label,
855
+ xlim,
856
+ )
857
+ single_heatmap_list.append(heatmap)
858
+ for indie in new_index:
859
+ xd = new_index.index(indie)
860
+ if transpose_index[xd] == 0:
861
+ heatmap = make_dot2(
862
+ double_list[indie],
863
+ f"{double_names[indie][0]}_{double_names[indie][1]}",
864
+ palette,
865
+ palette_orientation,
866
+ custom_colors,
867
+ axes_label,
868
+ xlim,
869
+ )
870
+ normal_heatmap_list.append(heatmap)
871
+ heatmap_t = make_dot3(
872
+ double_list[indie],
873
+ f"{double_names[indie][1]}_{double_names[indie][0]}",
874
+ palette,
875
+ palette_orientation,
876
+ custom_colors,
877
+ axes_label,
878
+ xlim,
879
+ )
880
+ transpose_heatmap_list.append(heatmap_t)
881
+ else:
882
+ heatmap = make_dot3(
883
+ double_list[indie],
884
+ f"{double_names[indie][0]}_{double_names[indie][1]}",
885
+ palette,
886
+ palette_orientation,
887
+ custom_colors,
888
+ axes_label,
889
+ xlim,
890
+ )
891
+ normal_heatmap_list.append(heatmap)
892
+ heatmap_t = make_dot2(
893
+ double_list[indie],
894
+ f"{double_names[indie][1]}_{double_names[indie][0]}",
895
+ palette,
896
+ palette_orientation,
897
+ custom_colors,
898
+ axes_label,
899
+ xlim,
900
+ )
901
+ transpose_heatmap_list.append(heatmap_t)
902
+
903
+ assert len(transpose_heatmap_list) == len(normal_heatmap_list)
904
+ single_length = len(single_heatmap_list)
905
+ if single_length == 0:
906
+ single_length = reverse_pascal(len(normal_heatmap_list))
907
+
908
+ normal_counter = 0
909
+ trans_counter = 0
910
+ trans_to_use = transpose_order(normal_heatmap_list)
911
+ start_grid = pw.Brick(figsize=(9, 9))
912
+ n = single_length * single_length
913
+
914
+ if n > 4:
915
+ print(f"This might take a while\n...\n")
916
+
917
+ printProgressBar(0, n, prefix="Progress:", suffix="Complete", length=40)
918
+ tots = 0
919
+ col_names = pw.Brick(figsize=(2, 9))
920
+ row_names = pw.Brick(figsize=(9, 2))
921
+ for i in range(single_length):
922
+ row_grid = pw.Brick(figsize=(9, 9))
923
+ for j in range(single_length):
924
+ if i == j:
925
+ if len(single_heatmap_list) == 0:
926
+ g1 = pw.Brick(figsize=(9, 9))
927
+ else:
928
+ g1 = pw.load_ggplot(single_heatmap_list[i], figsize=(9, 9))
929
+
930
+ elif i < j:
931
+ g1 = pw.load_ggplot(normal_heatmap_list[normal_counter], figsize=(9, 9))
932
+ normal_counter += 1
933
+ elif i > j:
934
+ g1 = pw.load_ggplot(
935
+ transpose_heatmap_list[trans_to_use[trans_counter]], figsize=(9, 9)
936
+ )
937
+ trans_counter += 1
938
+ if j == 0:
939
+ row_grid = g1
940
+ else:
941
+ row_grid = row_grid | g1
942
+ tots += 1
943
+ printProgressBar(tots, n, prefix="Progress:", suffix="Complete", length=40)
944
+ if i == 0:
945
+ start_grid = row_grid
946
+ else:
947
+ start_grid = row_grid / start_grid
948
+ for w in range(single_length):
949
+ p1 = (
950
+ ggplot()
951
+ + geom_blank()
952
+ + annotate( # Use geom_blank to create a plot with no data
953
+ "text",
954
+ x=0,
955
+ y=0,
956
+ label=single_names[w],
957
+ size=32,
958
+ angle=90,
959
+ ha="center",
960
+ va="center",
961
+ )
962
+ + theme(
963
+ # Center the plot area and make backgrounds transparent
964
+ axis_title_x=element_blank(),
965
+ axis_title_y=element_blank(),
966
+ axis_ticks=element_blank(),
967
+ axis_text=element_blank(),
968
+ plot_background=element_rect(
969
+ fill="none"
970
+ ), # Transparent plot background
971
+ panel_background=element_rect(
972
+ fill="none"
973
+ ), # Transparent panel background
974
+ panel_grid=element_blank(),
975
+ aspect_ratio=0.5, # Adjust the aspect ratio for the desired width/height
976
+ )
977
+ + coord_flip() # Rotate the plot 90 degrees counterclockwise
978
+ )
979
+ p2 = (
980
+ ggplot()
981
+ + geom_blank()
982
+ + annotate( # Use geom_blank to create a plot with no data
983
+ "text",
984
+ x=0,
985
+ y=0,
986
+ label=single_names[w],
987
+ size=32,
988
+ ha="center",
989
+ va="center",
990
+ )
991
+ + theme(
992
+ # Center the plot area and make backgrounds transparent
993
+ axis_title_x=element_blank(),
994
+ axis_title_y=element_blank(),
995
+ axis_ticks=element_blank(),
996
+ axis_text=element_blank(),
997
+ plot_background=element_rect(
998
+ fill="none"
999
+ ), # Transparent plot background
1000
+ panel_background=element_rect(
1001
+ fill="none"
1002
+ ), # Transparent panel background
1003
+ panel_grid=element_blank(),
1004
+ aspect_ratio=0.5, # Adjust the aspect ratio for the desired width/height
1005
+ )
1006
+ )
1007
+ g1 = pw.load_ggplot(p1, figsize=(2, 9))
1008
+ g2 = pw.load_ggplot(p2, figsize=(9, 2))
1009
+
1010
+ if w == 0:
1011
+ col_names = g1
1012
+ row_names = g2
1013
+ else:
1014
+ col_names = g1 / col_names
1015
+ row_names = row_names | g2
1016
+ # Create a ghost 2x2
1017
+ pghost = (
1018
+ ggplot()
1019
+ + geom_blank()
1020
+ + annotate( # Use geom_blank to create a plot with no data
1021
+ "text", x=0, y=0, label="", size=32, ha="center", va="center"
1022
+ )
1023
+ + theme(
1024
+ # Center the plot area and make backgrounds transparent
1025
+ axis_title_x=element_blank(),
1026
+ axis_title_y=element_blank(),
1027
+ axis_ticks=element_blank(),
1028
+ axis_text=element_blank(),
1029
+ plot_background=element_rect(fill="none"), # Transparent plot background
1030
+ panel_background=element_rect(fill="none"), # Transparent panel background
1031
+ panel_grid=element_blank(),
1032
+ aspect_ratio=0.5, # Adjust the aspect ratio for the desired width/height
1033
+ )
1034
+ )
1035
+ ghosty = pw.load_ggplot(pghost, figsize=(2, 2))
1036
+ col_names = ghosty / col_names
1037
+ start_grid = col_names | (row_names / start_grid)
1038
+ gridname = f"{single_length}x{single_length}_GRID"
1039
+ print(f"\nGrid complete! Saving to {directory}/{gridname}...\n")
1040
+ start_grid.savefig(f"{directory}/{gridname}.png")
1041
+ start_grid.savefig(f"{directory}/{gridname}.pdf", format="pdf")
1042
+ print(f"Grid saved successfully!\n")
1043
+
1044
+
1045
+ def create_plots(
1046
+ sdf,
1047
+ directory,
1048
+ name_x,
1049
+ name_y,
1050
+ palette,
1051
+ palette_orientation,
1052
+ no_hist,
1053
+ width,
1054
+ dpi,
1055
+ is_freq,
1056
+ xlim,
1057
+ custom_colors,
1058
+ custom_breakpoints,
1059
+ from_file,
1060
+ is_pairwise,
1061
+ axes_labels,
1062
+ ):
1063
+ df = read_df(
1064
+ sdf,
1065
+ palette,
1066
+ palette_orientation,
1067
+ is_freq,
1068
+ custom_colors,
1069
+ custom_breakpoints,
1070
+ from_file,
1071
+ )
1072
+ sdf = df
1073
+ plot_filename = f"{directory}/{name_x}"
1074
+ if is_pairwise:
1075
+ plot_filename = f"{directory}/{name_x}_{name_y}"
1076
+
1077
+ histy = make_hist(
1078
+ sdf, palette, palette_orientation, custom_colors, custom_breakpoints
1079
+ )
1080
+
1081
+ if is_pairwise:
1082
+ heatmap = make_dot(
1083
+ sdf,
1084
+ plot_filename,
1085
+ palette,
1086
+ palette_orientation,
1087
+ custom_colors,
1088
+ axes_labels,
1089
+ xlim,
1090
+ )
1091
+ print(f"Creating plots and saving to {plot_filename}...\n")
1092
+ ggsave(
1093
+ heatmap,
1094
+ width=9,
1095
+ height=9,
1096
+ dpi=dpi,
1097
+ format="pdf",
1098
+ filename=f"{plot_filename}_COMPARE.pdf",
1099
+ verbose=False,
1100
+ )
1101
+ ggsave(
1102
+ heatmap,
1103
+ width=9,
1104
+ height=9,
1105
+ dpi=dpi,
1106
+ format="png",
1107
+ filename=f"{plot_filename}_COMPARE.png",
1108
+ verbose=False,
1109
+ )
1110
+ try:
1111
+ if not heatmap.data:
1112
+ print(
1113
+ f"{plot_filename}_COMPARE.pdf and {plot_filename}_COMPARE.png saved sucessfully. \n"
1114
+ )
1115
+ return 0
1116
+ except ValueError:
1117
+ print(
1118
+ f"{plot_filename}_COMPARE.pdf and {plot_filename}_COMPARE.png saved sucessfully. \n"
1119
+ )
1120
+ return 0
1121
+ if no_hist:
1122
+ print(
1123
+ f"{plot_filename}_COMPARE.pdf and {plot_filename}_COMPARE.png saved sucessfully. \n"
1124
+ )
1125
+ else:
1126
+ ggsave(
1127
+ histy,
1128
+ width=3,
1129
+ height=3,
1130
+ dpi=dpi,
1131
+ format="pdf",
1132
+ filename=f"{plot_filename}_HIST.pdf",
1133
+ verbose=False,
1134
+ )
1135
+ ggsave(
1136
+ histy,
1137
+ width=3,
1138
+ height=3,
1139
+ dpi=dpi,
1140
+ format="png",
1141
+ filename=f"{plot_filename}_HIST.png",
1142
+ verbose=False,
1143
+ )
1144
+ print(
1145
+ f"{plot_filename}_COMPARE.pdf, {plot_filename}_COMPARE.png, {plot_filename}_HIST.pdf and {plot_filename}_HIST.png saved sucessfully. \n"
1146
+ )
1147
+ # Self-identity plots: Output _TRI, _FULL, and _HIST
1148
+ else:
1149
+ tri_plot = make_tri(
1150
+ sdf,
1151
+ plot_filename,
1152
+ palette,
1153
+ palette_orientation,
1154
+ custom_colors,
1155
+ axes_labels,
1156
+ xlim,
1157
+ )
1158
+ tri_plot_axis_only = make_tri2(
1159
+ sdf,
1160
+ plot_filename,
1161
+ palette,
1162
+ palette_orientation,
1163
+ custom_colors,
1164
+ axes_labels,
1165
+ xlim,
1166
+ )
1167
+ full_plot = make_dot(
1168
+ check_st_en_equality(sdf),
1169
+ plot_filename,
1170
+ palette,
1171
+ palette_orientation,
1172
+ custom_colors,
1173
+ axes_labels,
1174
+ xlim,
1175
+ )
1176
+ print(f"Creating plots and saving to {plot_filename}...\n")
1177
+ triplot_no_axis = tri_plot + theme(
1178
+ axis_text_x=element_blank(),
1179
+ axis_text_y=element_blank(),
1180
+ axis_title_x=element_blank(),
1181
+ axis_title_y=element_blank(),
1182
+ axis_line_x=element_blank(),
1183
+ axis_line_y=element_blank(),
1184
+ axis_ticks_major=element_blank(),
1185
+ axis_ticks_minor=element_blank(),
1186
+ panel_background=element_blank(),
1187
+ panel_grid_major=element_blank(),
1188
+ panel_grid_minor=element_blank(),
1189
+ plot_title=element_blank(),
1190
+ )
1191
+ ggsave(
1192
+ triplot_no_axis,
1193
+ width=9,
1194
+ height=9,
1195
+ dpi=600,
1196
+ format="png",
1197
+ filename=f"{plot_filename}_TRI_NOAXIS.png",
1198
+ verbose=False,
1199
+ )
1200
+ ggsave(
1201
+ tri_plot_axis_only,
1202
+ width=9,
1203
+ height=9,
1204
+ dpi=600,
1205
+ format="png",
1206
+ filename=f"{plot_filename}_AXIS.png",
1207
+ verbose=False,
1208
+ )
1209
+
1210
+ png_no_axes = Image.open(f"{plot_filename}_TRI_NOAXIS.png")
1211
+ rotated_png = png_no_axes.rotate(315, expand=True)
1212
+
1213
+ rotated_png.save(f"{plot_filename}_ROTATED_TRI_NOAXIS.png")
1214
+ overlap_axis(rotated_png, f"{plot_filename}_AXIS.png", plot_filename)
1215
+
1216
+ if os.path.exists(f"{plot_filename}_ROTATED_TRI_NOAXIS.png"):
1217
+ os.remove(f"{plot_filename}_ROTATED_TRI_NOAXIS.png")
1218
+ if os.path.exists(f"{plot_filename}_TRI_NOAXIS.png"):
1219
+ os.remove(f"{plot_filename}_TRI_NOAXIS.png")
1220
+
1221
+ ggsave(
1222
+ full_plot,
1223
+ width=9,
1224
+ height=9,
1225
+ dpi=dpi,
1226
+ format="pdf",
1227
+ filename=f"{plot_filename}_FULL.pdf",
1228
+ verbose=False,
1229
+ )
1230
+ ggsave(
1231
+ full_plot,
1232
+ width=9,
1233
+ height=9,
1234
+ dpi=dpi,
1235
+ format="png",
1236
+ filename=f"{plot_filename}_FULL.png",
1237
+ verbose=False,
1238
+ )
1239
+ if no_hist:
1240
+ print(
1241
+ f"{plot_filename}_TRI.png, {plot_filename}_TRI.pdf, {plot_filename}_FULL.png and {plot_filename}_FULL.pdf saved sucessfully. \n"
1242
+ )
1243
+ else:
1244
+ ggsave(
1245
+ histy,
1246
+ width=3,
1247
+ height=3,
1248
+ dpi=dpi,
1249
+ format="pdf",
1250
+ filename=plot_filename + "_HIST.pdf",
1251
+ verbose=False,
1252
+ )
1253
+ ggsave(
1254
+ histy,
1255
+ width=3,
1256
+ height=3,
1257
+ dpi=dpi,
1258
+ format="png",
1259
+ filename=plot_filename + "_HIST.png",
1260
+ verbose=False,
1261
+ )
1262
+ print(
1263
+ f"{plot_filename}_TRI.png, {plot_filename}_TRI.pdf, {plot_filename}_FULL.png, {plot_filename}_FULL.pdf, {plot_filename}_HIST.png and {plot_filename}_HIST.pdf, saved sucessfully. \n"
1264
+ )