malariagen_data 2.1.0__tar.gz → 2.2.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: malariagen-data
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: A package for accessing MalariaGEN public data.
5
5
  License: MIT
6
6
  Author: Alistair Miles
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.7
12
12
  Classifier: Programming Language :: Python :: 3.8
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Requires-Dist: BioPython
15
+ Requires-Dist: bokeh
15
16
  Requires-Dist: dask[array]
16
17
  Requires-Dist: fsspec
17
18
  Requires-Dist: gcsfs
@@ -2250,6 +2250,46 @@ class Ag3:
2250
2250
  # check parameters
2251
2251
  _check_param_min_cohort_size(min_cohort_size)
2252
2252
 
2253
+ if isinstance(contig, str):
2254
+ contig = [contig]
2255
+
2256
+ df = pd.concat(
2257
+ [
2258
+ self._gene_cnv_frequencies(
2259
+ contig=c,
2260
+ cohorts=cohorts,
2261
+ sample_query=sample_query,
2262
+ cohorts_analysis=cohorts_analysis,
2263
+ min_cohort_size=min_cohort_size,
2264
+ species_analysis=species_analysis,
2265
+ sample_sets=sample_sets,
2266
+ drop_invariant=drop_invariant,
2267
+ max_coverage_variance=max_coverage_variance,
2268
+ )
2269
+ for c in contig
2270
+ ],
2271
+ axis=0,
2272
+ )
2273
+
2274
+ return df
2275
+
2276
+ def _gene_cnv_frequencies(
2277
+ self,
2278
+ *,
2279
+ contig,
2280
+ cohorts,
2281
+ sample_query,
2282
+ cohorts_analysis,
2283
+ min_cohort_size,
2284
+ species_analysis,
2285
+ sample_sets,
2286
+ drop_invariant,
2287
+ max_coverage_variance,
2288
+ ):
2289
+
2290
+ # sanity check - this function is one contig at a time
2291
+ assert isinstance(contig, str)
2292
+
2253
2293
  # load sample metadata
2254
2294
  df_samples = self.sample_metadata(
2255
2295
  sample_sets=sample_sets,
@@ -3342,8 +3382,9 @@ class Ag3:
3342
3382
 
3343
3383
  Parameters
3344
3384
  ----------
3345
- contig : str
3346
- Chromosome arm, e.g., "3R".
3385
+ contig : str or list of str
3386
+ Chromosome arm, e.g., "3R". Multiple values can be provided
3387
+ as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
3347
3388
  area_by : str
3348
3389
  Column name in the sample metadata to use to group samples spatially. E.g.,
3349
3390
  use "admin1_iso" or "admin1_name" to group by level 1 administrative divisions,
@@ -3388,6 +3429,52 @@ class Ag3:
3388
3429
  # check parameters
3389
3430
  _check_param_min_cohort_size(min_cohort_size)
3390
3431
 
3432
+ if isinstance(contig, str):
3433
+ contig = [contig]
3434
+
3435
+ ds = xarray_concat(
3436
+ [
3437
+ self._gene_cnv_frequencies_advanced(
3438
+ contig=c,
3439
+ area_by=area_by,
3440
+ period_by=period_by,
3441
+ sample_sets=sample_sets,
3442
+ sample_query=sample_query,
3443
+ min_cohort_size=min_cohort_size,
3444
+ variant_query=variant_query,
3445
+ drop_invariant=drop_invariant,
3446
+ max_coverage_variance=max_coverage_variance,
3447
+ ci_method=ci_method,
3448
+ cohorts_analysis=cohorts_analysis,
3449
+ species_analysis=species_analysis,
3450
+ )
3451
+ for c in contig
3452
+ ],
3453
+ dim="variants",
3454
+ )
3455
+
3456
+ return ds
3457
+
3458
+ def _gene_cnv_frequencies_advanced(
3459
+ self,
3460
+ *,
3461
+ contig,
3462
+ area_by,
3463
+ period_by,
3464
+ sample_sets,
3465
+ sample_query,
3466
+ min_cohort_size,
3467
+ variant_query,
3468
+ drop_invariant,
3469
+ max_coverage_variance,
3470
+ ci_method,
3471
+ cohorts_analysis,
3472
+ species_analysis,
3473
+ ):
3474
+
3475
+ # sanity check - here we deal with one contig only
3476
+ assert isinstance(contig, str)
3477
+
3391
3478
  # load sample metadata
3392
3479
  df_samples = self.sample_metadata(
3393
3480
  sample_sets=sample_sets,
@@ -3813,6 +3900,248 @@ class Ag3:
3813
3900
 
3814
3901
  return out
3815
3902
 
3903
+ def resolve_region(self, region):
3904
+ """Convert a genome region into a standard data structure.
3905
+
3906
+ Parameters
3907
+ ----------
3908
+ region: str
3909
+ Chromosome arm (e.g., "2L"), gene name (e.g., "AGAP007280") or genomic
3910
+ region defined with coordinates (e.g., "2L:44989425-44998059").
3911
+
3912
+ Returns
3913
+ -------
3914
+ out : Region
3915
+ A named tuple with attributes contig, start and end.
3916
+
3917
+ """
3918
+
3919
+ return resolve_region(self, region)
3920
+
3921
+ def plot_genes(
3922
+ self,
3923
+ region,
3924
+ width=700,
3925
+ height=120,
3926
+ show=True,
3927
+ toolbar_location="above",
3928
+ x_range=None,
3929
+ title="Genes",
3930
+ ):
3931
+ """@@TODO"""
3932
+
3933
+ import bokeh.models as bkmod
3934
+ import bokeh.plotting as bkplt
3935
+
3936
+ # handle region parameter - this determines the genome region to plot
3937
+ region = self.resolve_region(region)
3938
+ contig = region.contig
3939
+ start = region.start
3940
+ end = region.end
3941
+ if start is None:
3942
+ start = 0
3943
+ if end is None:
3944
+ end = len(self.genome_sequence(contig))
3945
+
3946
+ # define x axis range
3947
+ if x_range is None:
3948
+ x_range = bkmod.Range1d(start, end, bounds="auto")
3949
+
3950
+ # select the genes overlapping the requested region
3951
+ df_geneset = self.geneset(attributes=["ID", "Name", "Parent", "description"])
3952
+ # df_geneset = df_geneset.set_index("ID")
3953
+ data = df_geneset.query(
3954
+ f"type == 'gene' and contig == '{contig}' and start < {end} and end > {start}"
3955
+ ).copy()
3956
+
3957
+ # we're going to plot each gene as a rectangle, so add some additional
3958
+ # columns
3959
+ data["bottom"] = np.where(data["strand"] == "+", 1, 0)
3960
+ data["top"] = data["bottom"] + 0.8
3961
+
3962
+ # tidy up some columns for presentation
3963
+ data["Name"].fillna("", inplace=True)
3964
+ data["description"].fillna("", inplace=True)
3965
+
3966
+ # define tooltips for hover
3967
+ tooltips = [
3968
+ ("ID", "@ID"),
3969
+ ("Name", "@Name"),
3970
+ ("Description", "@description"),
3971
+ ("Location", "@contig:@start{,}-@end{,}"),
3972
+ ]
3973
+
3974
+ # make a figure
3975
+ fig = bkplt.figure(
3976
+ title=title,
3977
+ plot_width=width,
3978
+ plot_height=height,
3979
+ tools="xpan,xzoom_in,xzoom_out,xwheel_zoom,reset,tap,hover",
3980
+ toolbar_location=toolbar_location,
3981
+ active_scroll="xwheel_zoom",
3982
+ active_drag="xpan",
3983
+ tooltips=tooltips,
3984
+ x_range=x_range,
3985
+ )
3986
+ fig.toolbar.logo = None
3987
+
3988
+ # add functionality to click through to vectorbase
3989
+ url = "https://vectorbase.org/vectorbase/app/record/gene/@ID"
3990
+ taptool = fig.select(type=bkmod.TapTool)
3991
+ taptool.callback = bkmod.OpenURL(url=url)
3992
+
3993
+ # now plot the genes as rectangles
3994
+ fig.quad(
3995
+ bottom="bottom",
3996
+ top="top",
3997
+ left="start",
3998
+ right="end",
3999
+ source=data,
4000
+ line_width=0.5,
4001
+ fill_alpha=0.5,
4002
+ )
4003
+
4004
+ # tidy up the plot
4005
+ fig.xaxis.axis_label = f"Contig {contig} position (bp)"
4006
+ fig.y_range = bkmod.Range1d(-0.4, 2.2)
4007
+ fig.ygrid.visible = False
4008
+ yticks = [0.4, 1.4]
4009
+ yticklabels = ["-", "+"]
4010
+ fig.yaxis.ticker = yticks
4011
+ fig.yaxis.major_label_overrides = {k: v for k, v in zip(yticks, yticklabels)}
4012
+ fig.xaxis[0].formatter = bkmod.NumeralTickFormatter(format="0,0")
4013
+
4014
+ if show:
4015
+ bkplt.show(fig)
4016
+
4017
+ return fig
4018
+
4019
+ def plot_transcript(
4020
+ self,
4021
+ transcript,
4022
+ width=700,
4023
+ height=120,
4024
+ show=True,
4025
+ x_range=None,
4026
+ toolbar_location="above",
4027
+ title=True,
4028
+ ):
4029
+
4030
+ import bokeh.models as bkmod
4031
+ import bokeh.plotting as bkplt
4032
+
4033
+ # find the transcript annotation
4034
+ df_geneset = self.geneset().set_index("ID")
4035
+ parent = df_geneset.loc[transcript]
4036
+
4037
+ if title is True:
4038
+ title = f"{transcript} ({parent.strand})"
4039
+
4040
+ # define tooltips for hover
4041
+ tooltips = [
4042
+ ("Type", "@type"),
4043
+ ("Location", "@contig:@start{,}-@end{,}"),
4044
+ ]
4045
+
4046
+ # make a figure
4047
+ fig = bkplt.figure(
4048
+ title=title,
4049
+ plot_width=width,
4050
+ plot_height=height,
4051
+ tools="xpan,xzoom_in,xzoom_out,xwheel_zoom,reset,hover",
4052
+ toolbar_location=toolbar_location,
4053
+ active_scroll="xwheel_zoom",
4054
+ active_drag="xpan",
4055
+ tooltips=tooltips,
4056
+ x_range=x_range,
4057
+ )
4058
+ fig.toolbar.logo = None
4059
+
4060
+ # find child components of the transcript
4061
+ data = df_geneset.set_index("Parent").loc[transcript].copy()
4062
+ data["bottom"] = -0.4
4063
+ data["top"] = 0.4
4064
+
4065
+ # plot exons
4066
+ exons = data.query("type == 'exon'")
4067
+ fig.quad(
4068
+ bottom="bottom",
4069
+ top="top",
4070
+ left="start",
4071
+ right="end",
4072
+ source=exons,
4073
+ fill_color=None,
4074
+ line_color="black",
4075
+ line_width=0.5,
4076
+ fill_alpha=0,
4077
+ )
4078
+
4079
+ # plot introns
4080
+ for intron_start, intron_end in zip(exons[:-1]["end"], exons[1:]["start"]):
4081
+ intron_midpoint = (intron_start + intron_end) / 2
4082
+ line_data = pd.DataFrame(
4083
+ {
4084
+ "x": [intron_start, intron_midpoint, intron_end],
4085
+ "y": [0, 0.1, 0],
4086
+ "type": "intron",
4087
+ "contig": parent.contig,
4088
+ "start": intron_start,
4089
+ "end": intron_end,
4090
+ }
4091
+ )
4092
+ fig.line(
4093
+ x="x",
4094
+ y="y",
4095
+ source=line_data,
4096
+ line_width=1,
4097
+ line_color="black",
4098
+ )
4099
+
4100
+ # plot UTRs
4101
+ fig.quad(
4102
+ bottom="bottom",
4103
+ top="top",
4104
+ left="start",
4105
+ right="end",
4106
+ source=data.query("type == 'five_prime_UTR'"),
4107
+ fill_color="green",
4108
+ line_width=0,
4109
+ fill_alpha=0.5,
4110
+ )
4111
+ fig.quad(
4112
+ bottom="bottom",
4113
+ top="top",
4114
+ left="start",
4115
+ right="end",
4116
+ source=data.query("type == 'three_prime_UTR'"),
4117
+ fill_color="red",
4118
+ line_width=0,
4119
+ fill_alpha=0.5,
4120
+ )
4121
+
4122
+ # plot CDSs
4123
+ fig.quad(
4124
+ bottom="bottom",
4125
+ top="top",
4126
+ left="start",
4127
+ right="end",
4128
+ source=data.query("type == 'CDS'"),
4129
+ fill_color="blue",
4130
+ line_width=0,
4131
+ fill_alpha=0.5,
4132
+ )
4133
+
4134
+ # tidy up the figure
4135
+ fig.yaxis.ticker = []
4136
+ fig.y_range = bkmod.Range1d(-0.6, 0.6)
4137
+ fig.xaxis.axis_label = f"Contig {parent.contig} position (bp)"
4138
+ fig.xaxis[0].formatter = bkmod.NumeralTickFormatter(format="0,0")
4139
+
4140
+ if show:
4141
+ bkplt.show(fig)
4142
+
4143
+ return fig
4144
+
3816
4145
 
3817
4146
  @numba.njit("Tuple((int8, int64))(int8[:], int8)")
3818
4147
  def _cn_mode_1d(a, vmax):
@@ -404,3 +404,35 @@ def xarray_concat(
404
404
  join=join,
405
405
  **kwargs,
406
406
  )
407
+
408
+
409
+ def type_error(
410
+ name,
411
+ value,
412
+ expectation,
413
+ ):
414
+ message = (
415
+ f"Bad type for parameter {name}; expected {expectation}, "
416
+ f"found {type(value)}"
417
+ )
418
+ raise TypeError(message)
419
+
420
+
421
+ def value_error(
422
+ name,
423
+ value,
424
+ expectation,
425
+ ):
426
+ message = (
427
+ f"Bad value for parameter {name}; expected {expectation}, " f"found {value!r}"
428
+ )
429
+ raise ValueError(message)
430
+
431
+
432
+ def check_type(
433
+ name,
434
+ value,
435
+ expectation,
436
+ ):
437
+ if not isinstance(value, expectation):
438
+ type_error(name, value, expectation)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "malariagen_data"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "A package for accessing MalariaGEN public data."
5
5
  authors = ["Alistair Miles <alistair.miles@sanger.ac.uk>", "Chris Clarkson <cc28@sanger.ac.uk>", "Kathryn Murie <km22@sanger.ac.uk>", "Nace Kranjc <n.kranjc@imperial.ac.uk>", "Kelly Bennett <kb25@sanger.ac.uk>"]
6
6
  license = "MIT"
@@ -19,6 +19,7 @@ scikit-allel = "*"
19
19
  xarray = "*"
20
20
  numba = "*"
21
21
  plotly = "*"
22
+ bokeh = "*"
22
23
  statsmodels = "*"
23
24
  ipyleaflet = "*"
24
25
  ipywidgets = "*"
@@ -9,6 +9,7 @@ package_data = \
9
9
 
10
10
  install_requires = \
11
11
  ['BioPython',
12
+ 'bokeh',
12
13
  'dask[array]',
13
14
  'fsspec',
14
15
  'gcsfs',
@@ -27,7 +28,7 @@ install_requires = \
27
28
 
28
29
  setup_kwargs = {
29
30
  'name': 'malariagen-data',
30
- 'version': '2.1.0',
31
+ 'version': '2.2.0',
31
32
  'description': 'A package for accessing MalariaGEN public data.',
32
33
  'long_description': None,
33
34
  'author': 'Alistair Miles',
File without changes