malariagen_data 2.0.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.0.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
@@ -4,15 +4,13 @@ from collections import Counter
4
4
 
5
5
  import allel
6
6
  import dask.array as da
7
- import ipyleaflet
8
- import ipywidgets
9
7
  import numba
10
8
  import numpy as np
11
9
  import pandas as pd
12
- import plotly.express as px
13
10
  import xarray as xr
14
11
  import zarr
15
- from statsmodels.stats.proportion import proportion_confint
12
+
13
+ import malariagen_data
16
14
 
17
15
  from . import veff
18
16
  from .util import (
@@ -145,15 +143,16 @@ class Ag3:
145
143
  def __repr__(self):
146
144
  return (
147
145
  f"<MalariaGEN Ag3 data resource API>\n"
148
- f"Storage URL : {self._url}\n"
149
- f"Releases available : {', '.join(self.releases)}\n"
150
- f"Cohorts analysis : {DEFAULT_COHORTS_ANALYSIS}\n"
151
- f"Species analysis : {DEFAULT_SPECIES_ANALYSIS}\n"
152
- f"Site filters analysis : {DEFAULT_SITE_FILTERS_ANALYSIS}\n"
146
+ f"Storage URL : {self._url}\n"
147
+ f"Data releases available : {', '.join(self.releases)}\n"
148
+ f"Cohorts analysis : {DEFAULT_COHORTS_ANALYSIS}\n"
149
+ f"Species analysis : {DEFAULT_SPECIES_ANALYSIS}\n"
150
+ f"Site filters analysis : {DEFAULT_SITE_FILTERS_ANALYSIS}\n"
151
+ f"Software version : {malariagen_data.__version__}\n"
153
152
  f"---\n"
154
153
  f"Please note that data are subject to terms of use,\n"
155
154
  f"for more information see https://www.malariagen.net/data\n"
156
- f"or contact data@malariagen.net. For API documentation see: "
155
+ f"or contact data@malariagen.net. For API documentation see: \n"
157
156
  f"https://malariagen.github.io/vector-data/ag3/api.html"
158
157
  )
159
158
 
@@ -173,10 +172,11 @@ class Ag3:
173
172
  </thead>
174
173
  <tbody>
175
174
  <tr><th style="text-align: left">Storage URL</th><td>{self._url}</td></tr>
176
- <tr><th style="text-align: left">Releases available</th><td>{', '.join(self.releases)}</td></tr>
175
+ <tr><th style="text-align: left">Data releases available</th><td>{', '.join(self.releases)}</td></tr>
177
176
  <tr><th style="text-align: left">Cohorts analysis</th><td>{DEFAULT_COHORTS_ANALYSIS}</td></tr>
178
177
  <tr><th style="text-align: left">Species analysis</th><td>{DEFAULT_SPECIES_ANALYSIS}</td></tr>
179
178
  <tr><th style="text-align: left">Site filters analysis</th><td>{DEFAULT_SITE_FILTERS_ANALYSIS}</td></tr>
179
+ <tr><th style="text-align: left">Software version</th><td>{malariagen_data.__version__}</td></tr>
180
180
  </tbody>
181
181
  </table>
182
182
  """
@@ -1287,7 +1287,11 @@ class Ag3:
1287
1287
  ann.get_effects(transcript=transcript, variants=df_snps)
1288
1288
 
1289
1289
  # add label
1290
- df_snps["label"] = df_snps.apply(_make_snp_label_effect, axis="columns")
1290
+ df_snps["label"] = _pandas_apply(
1291
+ _make_snp_label_effect,
1292
+ df_snps,
1293
+ columns=["contig", "position", "ref_allele", "alt_allele", "aa_change"],
1294
+ )
1291
1295
 
1292
1296
  # set index
1293
1297
  df_snps.set_index(
@@ -1298,7 +1302,11 @@ class Ag3:
1298
1302
  else:
1299
1303
 
1300
1304
  # add label
1301
- df_snps["label"] = df_snps.apply(_make_snp_label, axis="columns")
1305
+ df_snps["label"] = _pandas_apply(
1306
+ _make_snp_label,
1307
+ df_snps,
1308
+ columns=["contig", "position", "ref_allele", "alt_allele"],
1309
+ )
1302
1310
 
1303
1311
  # set index
1304
1312
  df_snps.set_index(
@@ -1709,7 +1717,7 @@ class Ag3:
1709
1717
 
1710
1718
  Parameters
1711
1719
  ----------
1712
- contig : str
1720
+ contig : str or list of str
1713
1721
  Chromosome arm, e.g., "3R". Multiple values can be provided
1714
1722
  as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
1715
1723
  sample_sets : str or list of str, optional
@@ -1884,7 +1892,7 @@ class Ag3:
1884
1892
 
1885
1893
  Parameters
1886
1894
  ----------
1887
- contig : str
1895
+ contig : str or list of str
1888
1896
  Chromosome arm, e.g., "3R". Multiple values can be provided
1889
1897
  as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
1890
1898
  sample_set : str
@@ -2037,7 +2045,7 @@ class Ag3:
2037
2045
 
2038
2046
  Parameters
2039
2047
  ----------
2040
- contig : str
2048
+ contig : str or list of str
2041
2049
  Chromosome arm, e.g., "3R". Multiple values can be provided
2042
2050
  as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
2043
2051
  sample_sets : str or list of str, optional
@@ -2088,8 +2096,9 @@ class Ag3:
2088
2096
 
2089
2097
  Parameters
2090
2098
  ----------
2091
- contig : str
2092
- Chromosome arm, e.g., "3R".
2099
+ contig : str or list of str
2100
+ Chromosome arm, e.g., "3R". Multiple values can be provided
2101
+ as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
2093
2102
  sample_sets : str or list of str
2094
2103
  Can be a sample set identifier (e.g., "AG1000G-AO") or a list of sample set
2095
2104
  identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"]) or a release identifier (e.g.,
@@ -2102,6 +2111,22 @@ class Ag3:
2102
2111
 
2103
2112
  """
2104
2113
 
2114
+ # handle multiple contigs
2115
+ if isinstance(contig, str):
2116
+ contig = [contig]
2117
+
2118
+ ds = xarray_concat(
2119
+ [self._gene_cnv(contig=c, sample_sets=sample_sets) for c in contig],
2120
+ dim="genes",
2121
+ )
2122
+
2123
+ return ds
2124
+
2125
+ def _gene_cnv(self, *, contig, sample_sets):
2126
+
2127
+ # sanity check
2128
+ assert isinstance(contig, str)
2129
+
2105
2130
  # access HMM data
2106
2131
  ds_hmm = self.cnv_hmm(contig=contig, sample_sets=sample_sets)
2107
2132
  pos = ds_hmm["variant_position"].values
@@ -2111,6 +2136,7 @@ class Ag3:
2111
2136
  # access genes
2112
2137
  df_geneset = self.geneset()
2113
2138
  df_genes = df_geneset.query(f"type == 'gene' and contig == '{contig}'")
2139
+ n_genes = len(df_genes)
2114
2140
 
2115
2141
  # setup intermediates
2116
2142
  windows = []
@@ -2143,14 +2169,16 @@ class Ag3:
2143
2169
  ds_out = xr.Dataset(
2144
2170
  coords={
2145
2171
  "gene_id": (["genes"], df_genes["ID"].values),
2146
- "gene_start": (["genes"], df_genes["start"].values),
2147
- "gene_end": (["genes"], df_genes["end"].values),
2148
2172
  "sample_id": (["samples"], ds_hmm["sample_id"].values),
2149
2173
  },
2150
2174
  data_vars={
2175
+ "gene_contig": (["genes"], np.array([contig] * n_genes, dtype=object)),
2176
+ "gene_start": (["genes"], df_genes["start"].values),
2177
+ "gene_end": (["genes"], df_genes["end"].values),
2151
2178
  "gene_windows": (["genes"], windows),
2152
2179
  "gene_name": (["genes"], df_genes["Name"].values),
2153
2180
  "gene_strand": (["genes"], df_genes["strand"].values),
2181
+ "gene_description": (["genes"], df_genes["description"].values),
2154
2182
  "CN_mode": (["genes", "samples"], modes),
2155
2183
  "CN_mode_count": (["genes", "samples"], counts),
2156
2184
  "sample_coverage_variance": (
@@ -2183,8 +2211,9 @@ class Ag3:
2183
2211
 
2184
2212
  Parameters
2185
2213
  ----------
2186
- contig : str
2187
- Chromosome arm, e.g., "3R".
2214
+ contig : str or list of str
2215
+ Chromosome arm, e.g., "3R". Multiple values can be provided
2216
+ as a list, in which case data will be concatenated, e.g., ["2R", "3R"].
2188
2217
  cohorts : str or dict
2189
2218
  If a string, gives the name of a predefined cohort set, e.g., one of
2190
2219
  {"admin1_month", "admin1_year", "admin2_month", "admin2_year"}.
@@ -2221,6 +2250,46 @@ class Ag3:
2221
2250
  # check parameters
2222
2251
  _check_param_min_cohort_size(min_cohort_size)
2223
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
+
2224
2293
  # load sample metadata
2225
2294
  df_samples = self.sample_metadata(
2226
2295
  sample_sets=sample_sets,
@@ -2245,9 +2314,6 @@ class Ag3:
2245
2314
  else:
2246
2315
  loc_samples = loc_pass_samples
2247
2316
 
2248
- # get genes
2249
- df_genes = self.geneset().query(f"type == 'gene' and contig == '{contig}'")
2250
-
2251
2317
  # figure out expected copy number
2252
2318
  if contig == "X":
2253
2319
  is_male = (df_samples["sex_call"] == "M").values
@@ -2255,20 +2321,25 @@ class Ag3:
2255
2321
  else:
2256
2322
  expected_cn = 2
2257
2323
 
2258
- # setup output dataframe
2259
- n_genes = len(df_genes)
2324
+ # setup output dataframe - two rows for each gene, one for amplification and one for deletion
2325
+ n_genes = ds_cnv.dims["genes"]
2326
+ df_genes = ds_cnv[
2327
+ [
2328
+ "gene_id",
2329
+ "gene_name",
2330
+ "gene_strand",
2331
+ "gene_description",
2332
+ "gene_contig",
2333
+ "gene_start",
2334
+ "gene_end",
2335
+ ]
2336
+ ].to_dataframe()
2260
2337
  df = pd.concat([df_genes, df_genes], axis=0).reset_index(drop=True)
2261
-
2262
- # drop columns we don't need
2263
- df.drop(columns=["source", "type", "score", "phase", "Parent"], inplace=True)
2264
-
2265
- # rename some columns
2266
2338
  df.rename(
2267
2339
  columns={
2268
- "ID": "gene_id",
2269
- "Name": "gene_name",
2270
- "strand": "gene_strand",
2271
- "description": "gene_description",
2340
+ "gene_contig": "contig",
2341
+ "gene_start": "start",
2342
+ "gene_end": "end",
2272
2343
  },
2273
2344
  inplace=True,
2274
2345
  )
@@ -2329,9 +2400,12 @@ class Ag3:
2329
2400
  df.reset_index(drop=True, inplace=True)
2330
2401
  df = pd.concat([df, df_freqs, df_extras], axis=1)
2331
2402
  df.sort_values(["contig", "start", "cnv_type"], inplace=True)
2403
+ df.reset_index(drop=True, inplace=True)
2332
2404
 
2333
2405
  # add label
2334
- df["label"] = df.apply(_make_gene_cnv_label, axis="columns")
2406
+ df["label"] = _pandas_apply(
2407
+ _make_gene_cnv_label, df, columns=["gene_id", "gene_name", "cnv_type"]
2408
+ )
2335
2409
 
2336
2410
  # deal with invariants
2337
2411
  if drop_invariant:
@@ -2709,7 +2783,11 @@ class Ag3:
2709
2783
  df_aaf["max_af"] = df_aaf[freq_cols].max(axis=1)
2710
2784
 
2711
2785
  # add label
2712
- df_aaf["label"] = df_aaf.apply(_make_snp_label_aa, axis="columns")
2786
+ df_aaf["label"] = _pandas_apply(
2787
+ _make_snp_label_aa,
2788
+ df_aaf,
2789
+ columns=["aa_change", "contig", "position", "ref_allele", "alt_allele"],
2790
+ )
2713
2791
 
2714
2792
  # sort by genomic position
2715
2793
  df_aaf = df_aaf.sort_values(["position", "aa_change"])
@@ -2743,20 +2821,23 @@ class Ag3:
2743
2821
  title=True,
2744
2822
  **kwargs,
2745
2823
  ):
2746
-
2747
- """Plot a heatmap from a pandas DataFrame of frequencies, e.g., output from
2748
- `Ag3.snp_allele_frequencies()` or `Ag3.gene_cnv_frequencies()`. It's recommended to
2749
- filter the input DataFrame to just rows of interest, i.e., fewer rows than `max_len`.
2824
+ """Plot a heatmap from a pandas DataFrame of frequencies, e.g., output
2825
+ from `Ag3.snp_allele_frequencies()` or `Ag3.gene_cnv_frequencies()`.
2826
+ It's recommended to filter the input DataFrame to just rows of interest,
2827
+ i.e., fewer rows than `max_len`.
2750
2828
 
2751
2829
  Parameters
2752
2830
  ----------
2753
2831
  df : pandas DataFrame
2754
- A DataFrame of frequencies, e.g., output from `snp_allele_frequencies()` or `gene_cnv_frequencies()`.
2832
+ A DataFrame of frequencies, e.g., output from
2833
+ `snp_allele_frequencies()` or `gene_cnv_frequencies()`.
2755
2834
  index : str or list of str
2756
- One or more column headers that are present in the input dataframe. This becomes the heatmap y-axis
2757
- row labels. The column/s must produce a unique index.
2835
+ One or more column headers that are present in the input dataframe.
2836
+ This becomes the heatmap y-axis row labels. The column/s must
2837
+ produce a unique index.
2758
2838
  max_len : int, optional
2759
- Displaying large styled dataframes may cause ipython notebooks to crash.
2839
+ Displaying large styled dataframes may cause ipython notebooks to
2840
+ crash.
2760
2841
  x_label : str, optional
2761
2842
  This is the x-axis label that will be displayed on the heatmap.
2762
2843
  y_label : str, optional
@@ -2781,6 +2862,8 @@ class Ag3:
2781
2862
 
2782
2863
  """
2783
2864
 
2865
+ import plotly.express as px
2866
+
2784
2867
  # check len of input
2785
2868
  if len(df) > max_len:
2786
2869
  raise ValueError(f"Input DataFrame is longer than {max_len}")
@@ -2871,39 +2954,40 @@ class Ag3:
2871
2954
  species_analysis=DEFAULT_SPECIES_ANALYSIS,
2872
2955
  site_filters_analysis=DEFAULT_SITE_FILTERS_ANALYSIS,
2873
2956
  ):
2874
- """Group samples by taxon, area (space) and period (time), then compute SNP allele counts
2875
- and frequencies.
2957
+ """Group samples by taxon, area (space) and period (time), then compute
2958
+ SNP allele counts and frequencies.
2876
2959
 
2877
2960
  Parameters
2878
2961
  ----------
2879
2962
  transcript : str
2880
2963
  Gene transcript ID (AgamP4.12), e.g., "AGAP004707-RD".
2881
2964
  area_by : str
2882
- Column name in the sample metadata to use to group samples spatially. E.g.,
2883
- use "adm1_ISO" or "adm1_name" to group by level 1 administrative divisions,
2884
- or use "adm2_name" to group by level 2 administrative divisions.
2965
+ Column name in the sample metadata to use to group samples
2966
+ spatially. E.g., use "admin1_iso" or "admin1_name" to group by level 1
2967
+ administrative divisions, or use "admin2_name" to group by level 2
2968
+ administrative divisions.
2885
2969
  period_by : {"year", "quarter", "month"}
2886
2970
  Length of time to group samples temporally.
2887
2971
  sample_sets : str or list of str, optional
2888
- Can be a sample set identifier (e.g., "AG1000G-AO") or a list of sample set
2889
- identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"]) or a release identifier (e.g.,
2890
- "3.0") or a list of release identifiers.
2972
+ Can be a sample set identifier (e.g., "AG1000G-AO") or a list of
2973
+ sample set identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"]) or a
2974
+ release identifier (e.g., "3.0") or a list of release identifiers.
2891
2975
  sample_query : str, optional
2892
- A pandas query string which will be evaluated against the sample metadata e.g.,
2893
- "taxon == 'coluzzii' and country == 'Burkina Faso'".
2976
+ A pandas query string which will be evaluated against the sample
2977
+ metadata e.g., "taxon == 'coluzzii' and country == 'Burkina Faso'".
2894
2978
  min_cohort_size : int, optional
2895
2979
  Minimum cohort size. Any cohorts below this size are omitted.
2896
2980
  drop_invariant : bool, optional
2897
- If True, variants with no alternate allele calls in any cohorts are dropped from
2898
- the result.
2981
+ If True, variants with no alternate allele calls in any cohorts are
2982
+ dropped from the result.
2899
2983
  variant_query : str, optional
2900
2984
  site_mask : str, optional
2901
2985
  Site filters mask to apply.
2902
2986
  nobs_mode : {"called", "fixed"}
2903
- Method for calculating the denominator when computing frequencies. If "called"
2904
- then use the number of called alleles, i.e., number of samples with non-missing
2905
- genotype calls multiplied by 2. If "fixed" then use the number of samples
2906
- multiplied by 2.
2987
+ Method for calculating the denominator when computing frequencies.
2988
+ If "called" then use the number of called alleles, i.e., number of
2989
+ samples with non-missing genotype calls multiplied by 2. If "fixed"
2990
+ then use the number of samples multiplied by 2.
2907
2991
  ci_method : {"normal", "agresti_coull", "beta", "wilson", "binom_test"}, optional
2908
2992
  Method to use for computing confidence intervals, passed through to
2909
2993
  `statsmodels.stats.proportion.proportion_confint`.
@@ -2918,11 +3002,11 @@ class Ag3:
2918
3002
  -------
2919
3003
  ds : xarray.Dataset
2920
3004
  The resulting dataset contains data has dimensions "cohorts" and "variants".
2921
- Variables prefixed with "cohort_" are 1-dimensional arrays with data about
3005
+ Variables prefixed with "cohort" are 1-dimensional arrays with data about
2922
3006
  the cohorts, such as the area, period, taxon and cohort size. Variables
2923
- prefixed with "variant_" are 1-dimensional arrays with data about the variants,
3007
+ prefixed with "variant" are 1-dimensional arrays with data about the variants,
2924
3008
  such as the contig, position, reference and alternate alleles. Variables prefixed
2925
- with "event_" are 2-dimensional arrays with the allele counts and frequency
3009
+ with "event" are 2-dimensional arrays with the allele counts and frequency
2926
3010
  calculations.
2927
3011
 
2928
3012
  """
@@ -2976,8 +3060,8 @@ class Ag3:
2976
3060
  # bring genotypes into memory
2977
3061
  gt = gt.compute()
2978
3062
 
2979
- # convert genotypes to more convenient representation
2980
- gac, gan = _genotypes_to_alt_allele_counts_melt(gt, max_allele=3)
3063
+ # # convert genotypes to more convenient representation
3064
+ # gac, gan = _genotypes_to_alt_allele_counts_melt(gt, max_allele=3)
2981
3065
 
2982
3066
  # set up variant variables
2983
3067
  contigs = ds_snps.attrs["contigs"]
@@ -3013,13 +3097,18 @@ class Ag3:
3013
3097
  # compute cohort allele counts
3014
3098
  # cohort_gac = np.take(gac, sample_indices, axis=1)
3015
3099
  # np.sum(cohort_gac, axis=1, out=count[:, cohort_index])
3016
- count[:, cohort_index] = _take_sum_cols(gac, sample_indices)
3100
+ # count[:, cohort_index] = _take_sum_cols(gac, sample_indices)
3101
+ cohort_ac, cohort_an = _cohort_alt_allele_counts_melt(
3102
+ gt, sample_indices, max_allele=3
3103
+ )
3104
+ count[:, cohort_index] = cohort_ac
3017
3105
 
3018
3106
  # compute cohort allele numbers
3019
3107
  if nobs_mode == "called":
3020
3108
  # cohort_gan = np.take(gan, sample_indices, axis=1)
3021
3109
  # np.sum(cohort_gan, axis=1, out=nobs[:, cohort_index])
3022
- nobs[:, cohort_index] = _take_sum_cols(gan, sample_indices)
3110
+ # nobs[:, cohort_index] = _take_sum_cols(gan, sample_indices)
3111
+ nobs[:, cohort_index] = cohort_an
3023
3112
  elif nobs_mode == "fixed":
3024
3113
  nobs[:, cohort_index] = cohort.size * 2
3025
3114
  else:
@@ -3053,7 +3142,7 @@ class Ag3:
3053
3142
  # deal with SNP alleles not observed
3054
3143
  if drop_invariant:
3055
3144
  loc_variant = max_af > 0
3056
- df_variants = df_variants.loc[loc_variant]
3145
+ df_variants = df_variants.loc[loc_variant].reset_index(drop=True)
3057
3146
  count = np.compress(loc_variant, count, axis=0)
3058
3147
  nobs = np.compress(loc_variant, nobs, axis=0)
3059
3148
  frequency = np.compress(loc_variant, frequency, axis=0)
@@ -3065,7 +3154,11 @@ class Ag3:
3065
3154
  ann.get_effects(transcript=transcript, variants=df_variants)
3066
3155
 
3067
3156
  # add variant labels
3068
- df_variants["label"] = df_variants.apply(_make_snp_label_effect, axis="columns")
3157
+ df_variants["label"] = _pandas_apply(
3158
+ _make_snp_label_effect,
3159
+ df_variants,
3160
+ columns=["contig", "position", "ref_allele", "alt_allele", "aa_change"],
3161
+ )
3069
3162
 
3070
3163
  # build the output dataset
3071
3164
  ds_out = xr.Dataset()
@@ -3129,8 +3222,8 @@ class Ag3:
3129
3222
  Gene transcript ID (AgamP4.12), e.g., "AGAP004707-RD".
3130
3223
  area_by : str
3131
3224
  Column name in the sample metadata to use to group samples spatially. E.g.,
3132
- use "adm1_ISO" or "adm1_name" to group by level 1 administrative divisions,
3133
- or use "adm2_name" to group by level 2 administrative divisions.
3225
+ use "admin1_iso" or "admin1_name" to group by level 1 administrative divisions,
3226
+ or use "admin2_name" to group by level 2 administrative divisions.
3134
3227
  period_by : {"year", "quarter", "month"}
3135
3228
  Length of time to group samples temporally.
3136
3229
  sample_sets : str or list of str, optional
@@ -3164,11 +3257,11 @@ class Ag3:
3164
3257
  -------
3165
3258
  ds : xarray.Dataset
3166
3259
  The resulting dataset contains data has dimensions "cohorts" and "variants".
3167
- Variables prefixed with "cohort_" are 1-dimensional arrays with data about
3260
+ Variables prefixed with "cohort" are 1-dimensional arrays with data about
3168
3261
  the cohorts, such as the area, period, taxon and cohort size. Variables
3169
- prefixed with "variant_" are 1-dimensional arrays with data about the variants,
3262
+ prefixed with "variant" are 1-dimensional arrays with data about the variants,
3170
3263
  such as the contig, position, reference and alternate alleles. Variables prefixed
3171
- with "event_" are 2-dimensional arrays with the allele counts and frequency
3264
+ with "event" are 2-dimensional arrays with the allele counts and frequency
3172
3265
  calculations.
3173
3266
 
3174
3267
  """
@@ -3241,7 +3334,11 @@ class Ag3:
3241
3334
  df_variants.columns = [c.split("variant_")[1] for c in df_variants.columns]
3242
3335
 
3243
3336
  # assign new variant label
3244
- label = df_variants.apply(_make_snp_label_aa, axis=1)
3337
+ label = _pandas_apply(
3338
+ _make_snp_label_aa,
3339
+ df_variants,
3340
+ columns=["aa_change", "contig", "position", "ref_allele", "alt_allele"],
3341
+ )
3245
3342
  ds_aa_frq["variant_label"] = "variants", label
3246
3343
 
3247
3344
  # apply variant query if given
@@ -3285,12 +3382,13 @@ class Ag3:
3285
3382
 
3286
3383
  Parameters
3287
3384
  ----------
3288
- contig : str
3289
- 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"].
3290
3388
  area_by : str
3291
3389
  Column name in the sample metadata to use to group samples spatially. E.g.,
3292
- use "adm1_ISO" or "adm1_name" to group by level 1 administrative divisions,
3293
- or use "adm2_name" to group by level 2 administrative divisions.
3390
+ use "admin1_iso" or "admin1_name" to group by level 1 administrative divisions,
3391
+ or use "admin2_name" to group by level 2 administrative divisions.
3294
3392
  period_by : {"year", "quarter", "month"}
3295
3393
  Length of time to group samples temporally.
3296
3394
  sample_sets : str or list of str, optional
@@ -3319,11 +3417,11 @@ class Ag3:
3319
3417
  -------
3320
3418
  ds : xarray.Dataset
3321
3419
  The resulting dataset contains data has dimensions "cohorts" and "variants".
3322
- Variables prefixed with "cohort_" are 1-dimensional arrays with data about
3420
+ Variables prefixed with "cohort" are 1-dimensional arrays with data about
3323
3421
  the cohorts, such as the area, period, taxon and cohort size. Variables
3324
- prefixed with "variant_" are 1-dimensional arrays with data about the variants,
3422
+ prefixed with "variant" are 1-dimensional arrays with data about the variants,
3325
3423
  such as the contig, position, reference and alternate alleles. Variables prefixed
3326
- with "event_" are 2-dimensional arrays with the allele counts and frequency
3424
+ with "event" are 2-dimensional arrays with the allele counts and frequency
3327
3425
  calculations.
3328
3426
 
3329
3427
  """
@@ -3331,6 +3429,52 @@ class Ag3:
3331
3429
  # check parameters
3332
3430
  _check_param_min_cohort_size(min_cohort_size)
3333
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
+
3334
3478
  # load sample metadata
3335
3479
  df_samples = self.sample_metadata(
3336
3480
  sample_sets=sample_sets,
@@ -3439,7 +3583,11 @@ class Ag3:
3439
3583
  )
3440
3584
 
3441
3585
  # add variant label
3442
- df_variants["label"] = df_variants.apply(_make_gene_cnv_label, axis="columns")
3586
+ df_variants["label"] = _pandas_apply(
3587
+ _make_gene_cnv_label,
3588
+ df_variants,
3589
+ columns=["gene_id", "gene_name", "cnv_type"],
3590
+ )
3443
3591
 
3444
3592
  # build the output dataset
3445
3593
  ds_out = xr.Dataset()
@@ -3461,7 +3609,7 @@ class Ag3:
3461
3609
  if drop_invariant:
3462
3610
  loc_variant = df_variants["max_af"].values > 0
3463
3611
  ds_out = ds_out.isel(variants=loc_variant)
3464
- df_variants = df_variants.loc[loc_variant]
3612
+ df_variants = df_variants.loc[loc_variant].reset_index(drop=True)
3465
3613
 
3466
3614
  # apply variant query
3467
3615
  if variant_query is not None:
@@ -3508,6 +3656,8 @@ class Ag3:
3508
3656
 
3509
3657
  """
3510
3658
 
3659
+ import plotly.express as px
3660
+
3511
3661
  # handle title
3512
3662
  if title is True:
3513
3663
  title = ds.attrs.get("title", None)
@@ -3610,6 +3760,9 @@ class Ag3:
3610
3760
 
3611
3761
  """
3612
3762
 
3763
+ import ipyleaflet
3764
+ import ipywidgets
3765
+
3613
3766
  # slice dataset to variant of interest
3614
3767
  if isinstance(variant, int):
3615
3768
  ds_variant = ds.isel(variants=variant)
@@ -3710,6 +3863,9 @@ class Ag3:
3710
3863
 
3711
3864
  """
3712
3865
 
3866
+ import ipyleaflet
3867
+ import ipywidgets
3868
+
3713
3869
  # handle title
3714
3870
  if title is True:
3715
3871
  title = ds.attrs.get("title", None)
@@ -3744,6 +3900,248 @@ class Ag3:
3744
3900
 
3745
3901
  return out
3746
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
+
3747
4145
 
3748
4146
  @numba.njit("Tuple((int8, int64))(int8[:], int8)")
3749
4147
  def _cn_mode_1d(a, vmax):
@@ -3818,109 +4216,59 @@ def _make_sample_period_year(row):
3818
4216
  return pd.NaT
3819
4217
 
3820
4218
 
3821
- # def _genotypes_to_alt_allele_counts_melt(gt, max_allele):
3822
- # """Convert a genotype array to an array of alt allele counts, melted to
3823
- # store one row per alt allele."""
3824
- #
3825
- # n_variants = gt.shape[0]
3826
- # n_samples = gt.shape[1]
3827
- #
3828
- # # convert to genotype allele counts
3829
- # gac = allel.GenotypeArray(gt).to_allele_counts(max_allele=max_allele)
3830
- # assert gac.shape == (n_variants, n_samples, max_allele + 1)
3831
- #
3832
- # # sum total observations over alleles
3833
- # gan = gac.sum(axis=2)
3834
- #
3835
- # # keep only alt allele counts
3836
- # gac_alt = gac[:, :, 1:]
3837
- # assert gac_alt.shape == (n_variants, n_samples, max_allele)
3838
- #
3839
- # # use some numpy tricks to melt alleles into rows
3840
- # gac_alt_melt = gac_alt.swapaxes(2, 1).reshape(-1, n_samples)
3841
- # assert gac_alt_melt.shape == (n_variants * max_allele, n_samples)
3842
- # gan_melt = np.repeat(gan, max_allele, axis=0)
3843
- # assert gan_melt.shape == (n_variants * max_allele, n_samples)
3844
- #
3845
- # return gac_alt_melt, gan_melt
3846
-
3847
-
3848
4219
  @numba.njit
3849
- def _genotypes_to_alt_allele_counts_melt_kernel(gt, max_allele):
3850
- """Convert a genotype array to an array of alt allele counts, melted to
3851
- store one row per alt allele."""
4220
+ def _cohort_alt_allele_counts_melt_kernel(gt, indices, max_allele):
3852
4221
 
3853
4222
  n_variants = gt.shape[0]
3854
- n_samples = gt.shape[1]
4223
+ n_indices = indices.shape[0]
3855
4224
  ploidy = gt.shape[2]
3856
4225
 
3857
- gac_alt_melt = np.zeros((n_variants * max_allele, n_samples), dtype=np.uint8)
3858
- gan = np.zeros((n_variants, n_samples), dtype=np.uint8)
4226
+ ac_alt_melt = np.zeros(n_variants * max_allele, dtype=np.int64)
4227
+ an = np.zeros(n_variants, dtype=np.int64)
3859
4228
 
3860
4229
  for i in range(n_variants):
3861
4230
  out_i_offset = (i * max_allele) - 1
3862
- for j in range(n_samples):
4231
+ for j in range(n_indices):
4232
+ ix = indices[j]
3863
4233
  for k in range(ploidy):
3864
- allele = gt[i, j, k]
4234
+ allele = gt[i, ix, k]
3865
4235
  if allele > 0:
3866
4236
  out_i = out_i_offset + allele
3867
- gac_alt_melt[out_i, j] += 1
3868
- gan[i, j] += 1
4237
+ ac_alt_melt[out_i] += 1
4238
+ an[i] += 1
3869
4239
  elif allele == 0:
3870
- gan[i, j] += 1
4240
+ an[i] += 1
3871
4241
 
3872
- return gac_alt_melt, gan
4242
+ return ac_alt_melt, an
3873
4243
 
3874
4244
 
3875
- def _genotypes_to_alt_allele_counts_melt(gt, max_allele):
3876
- gac_alt_melt, gan = _genotypes_to_alt_allele_counts_melt_kernel(gt, max_allele)
3877
- gan_melt = np.repeat(gan, max_allele, axis=0)
3878
- return gac_alt_melt, gan_melt
4245
+ def _cohort_alt_allele_counts_melt(gt, indices, max_allele):
4246
+ ac_alt_melt, an = _cohort_alt_allele_counts_melt_kernel(gt, indices, max_allele)
4247
+ an_melt = np.repeat(an, max_allele, axis=0)
4248
+ return ac_alt_melt, an_melt
3879
4249
 
3880
4250
 
3881
- @numba.njit
3882
- def _take_sum_cols(a, indices):
3883
- n_variants = a.shape[0]
3884
- n_indices = indices.shape[0]
3885
- out = np.zeros(n_variants, dtype=np.int64)
3886
- for i in range(n_variants):
3887
- v_sum = 0
3888
- for j in range(n_indices):
3889
- ix = indices[j]
3890
- v = a[i, ix]
3891
- v_sum += v
3892
- out[i] = v_sum
3893
- return out
3894
-
3895
-
3896
- def _make_snp_label(row):
3897
- label = (
3898
- f"{row['contig']}:{row['position']:,} {row['ref_allele']}>{row['alt_allele']}"
3899
- )
3900
- return label
4251
+ def _make_snp_label(contig, position, ref_allele, alt_allele):
4252
+ return f"{contig}:{position:,} {ref_allele}>{alt_allele}"
3901
4253
 
3902
4254
 
3903
- def _make_snp_label_effect(row):
3904
- label = (
3905
- f"{row['contig']}:{row['position']:,} {row['ref_allele']}>{row['alt_allele']}"
3906
- )
3907
- aa_change = row["aa_change"]
4255
+ def _make_snp_label_effect(contig, position, ref_allele, alt_allele, aa_change):
4256
+ label = f"{contig}:{position:,} {ref_allele}>{alt_allele}"
3908
4257
  if isinstance(aa_change, str):
3909
4258
  label += f" ({aa_change})"
3910
4259
  return label
3911
4260
 
3912
4261
 
3913
- def _make_snp_label_aa(row):
3914
- label = f"{row['aa_change']} ({row['contig']}:{row['position']:,} {row['ref_allele']}>{row['alt_allele']})"
4262
+ def _make_snp_label_aa(aa_change, contig, position, ref_allele, alt_allele):
4263
+ label = f"{aa_change} ({contig}:{position:,} {ref_allele}>{alt_allele})"
3915
4264
  return label
3916
4265
 
3917
4266
 
3918
- def _make_gene_cnv_label(row):
3919
- label = row["gene_id"]
3920
- gene_name = row["gene_name"]
4267
+ def _make_gene_cnv_label(gene_id, gene_name, cnv_type):
4268
+ label = gene_id
3921
4269
  if isinstance(gene_name, str):
3922
4270
  label += f" ({gene_name})"
3923
- label += f" {row['cnv_type']}"
4271
+ label += f" {cnv_type}"
3924
4272
  return label
3925
4273
 
3926
4274
 
@@ -3963,6 +4311,8 @@ def _map_snp_to_aa_change_frq_ds(ds):
3963
4311
 
3964
4312
 
3965
4313
  def _add_frequency_ci(ds, ci_method):
4314
+ from statsmodels.stats.proportion import proportion_confint
4315
+
3966
4316
  if ci_method is not None:
3967
4317
  count = ds["event_count"].values
3968
4318
  nobs = ds["event_nobs"].values
@@ -4047,3 +4397,11 @@ def _check_param_min_cohort_size(min_cohort_size):
4047
4397
  raise ValueError(
4048
4398
  f"Value of parameter min_cohort_size must be at least 1; found {min_cohort_size}."
4049
4399
  )
4400
+
4401
+
4402
+ def _pandas_apply(f, df, columns):
4403
+ """Optimised alternative to pandas apply."""
4404
+ df = df.reset_index(drop=True)
4405
+ iterator = zip(*[df[c].values for c in columns])
4406
+ ret = pd.Series((f(*vals) for vals in iterator))
4407
+ return ret
@@ -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,8 +1,8 @@
1
1
  [tool.poetry]
2
2
  name = "malariagen_data"
3
- version = "2.0.0"
3
+ version = "2.2.0"
4
4
  description = "A package for accessing MalariaGEN public data."
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>"]
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"
7
7
 
8
8
  [tool.poetry.dependencies]
@@ -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.0.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