protpy 1.4.1__tar.gz → 1.4.2__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.
Files changed (33) hide show
  1. {protpy-1.4.1/protpy.egg-info → protpy-1.4.2}/PKG-INFO +4 -4
  2. {protpy-1.4.1 → protpy-1.4.2}/docs/conf.py +1 -1
  3. {protpy-1.4.1 → protpy-1.4.2}/protpy/__init__.py +1 -1
  4. {protpy-1.4.1 → protpy-1.4.2}/protpy/autocorrelation.py +5 -5
  5. {protpy-1.4.1 → protpy-1.4.2}/protpy/composition.py +88 -96
  6. {protpy-1.4.1 → protpy-1.4.2}/protpy/encode.py +8 -1
  7. {protpy-1.4.1 → protpy-1.4.2}/protpy/sequence_order.py +18 -12
  8. {protpy-1.4.1 → protpy-1.4.2/protpy.egg-info}/PKG-INFO +4 -4
  9. {protpy-1.4.1 → protpy-1.4.2}/protpy.egg-info/requires.txt +3 -3
  10. {protpy-1.4.1 → protpy-1.4.2}/pyproject.toml +4 -4
  11. {protpy-1.4.1 → protpy-1.4.2}/tests/test_autocorrelation.py +1 -1
  12. {protpy-1.4.1 → protpy-1.4.2}/tests/test_composition.py +12 -12
  13. {protpy-1.4.1 → protpy-1.4.2}/tests/test_protpy.py +2 -2
  14. {protpy-1.4.1 → protpy-1.4.2}/LICENSE +0 -0
  15. {protpy-1.4.1 → protpy-1.4.2}/MANIFEST.in +0 -0
  16. {protpy-1.4.1 → protpy-1.4.2}/README.md +0 -0
  17. {protpy-1.4.1 → protpy-1.4.2}/protpy/_constants.py +0 -0
  18. {protpy-1.4.1 → protpy-1.4.2}/protpy/conjoint_triad.py +0 -0
  19. {protpy-1.4.1 → protpy-1.4.2}/protpy/ctd.py +0 -0
  20. {protpy-1.4.1 → protpy-1.4.2}/protpy/data/README.md +0 -0
  21. {protpy-1.4.1 → protpy-1.4.2}/protpy/data/grantham-physicochemical-distance-matrix.json +0 -0
  22. {protpy-1.4.1 → protpy-1.4.2}/protpy/data/schneider-wrede-physicochemical-distance-matrix.json +0 -0
  23. {protpy-1.4.1 → protpy-1.4.2}/protpy/utils.py +0 -0
  24. {protpy-1.4.1 → protpy-1.4.2}/protpy.egg-info/SOURCES.txt +0 -0
  25. {protpy-1.4.1 → protpy-1.4.2}/protpy.egg-info/dependency_links.txt +0 -0
  26. {protpy-1.4.1 → protpy-1.4.2}/protpy.egg-info/top_level.txt +0 -0
  27. {protpy-1.4.1 → protpy-1.4.2}/setup.cfg +0 -0
  28. {protpy-1.4.1 → protpy-1.4.2}/tests/__init__.py +0 -0
  29. {protpy-1.4.1 → protpy-1.4.2}/tests/test_conjoint_triad.py +0 -0
  30. {protpy-1.4.1 → protpy-1.4.2}/tests/test_ctd.py +0 -0
  31. {protpy-1.4.1 → protpy-1.4.2}/tests/test_encode.py +0 -0
  32. {protpy-1.4.1 → protpy-1.4.2}/tests/test_sequence_order.py +0 -0
  33. {protpy-1.4.1 → protpy-1.4.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: protpy
3
- Version: 1.4.1
3
+ Version: 1.4.2
4
4
  Summary: Python package for generating various biochemical, physicochemical and structural descriptors/features of protein sequences.
5
5
  Author-email: AJ McKenna <amckenna41@qub.ac.uk>
6
6
  Maintainer-email: AJ McKenna <amckenna41@qub.ac.uk>
@@ -32,9 +32,9 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
32
32
  Requires-Python: >=3.9
33
33
  Description-Content-Type: text/markdown
34
34
  License-File: LICENSE
35
- Requires-Dist: aaindex>=1.2.0
36
- Requires-Dist: numpy>=2.4.4
37
- Requires-Dist: pandas>=3.0.2
35
+ Requires-Dist: aaindex>=1.3.1
36
+ Requires-Dist: numpy>=2.0.0
37
+ Requires-Dist: pandas>=2.0.2
38
38
  Requires-Dist: tqdm>=4.0.0
39
39
  Requires-Dist: varname>=0.15.1
40
40
  Provides-Extra: test
@@ -13,7 +13,7 @@ sys.path.insert(0, os.path.abspath('..'))
13
13
  project = 'protpy'
14
14
  copyright = '2026, AJ McKenna'
15
15
  author = 'AJ McKenna'
16
- release = '1.4.1'
16
+ release = '1.4.2'
17
17
 
18
18
  # -- General configuration ---------------------------------------------------
19
19
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -12,7 +12,7 @@ amino_acids = ["A", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "P",
12
12
 
13
13
  #protPy package metadata
14
14
  __name__ = 'protpy'
15
- __version__ = "1.4.1"
15
+ __version__ = "1.4.2"
16
16
  __description__ = "Python package for generating various biochemical, physicochemical and structural descriptors/features of protein sequences."
17
17
  __author__ = 'AJ McKenna: https://github.com/amckenna41'
18
18
  __authorEmail__ = 'amckenna41@qub.ac.uk'
@@ -131,10 +131,10 @@ def moreaubroto_autocorrelation(sequence: str, lag: int = 30,
131
131
 
132
132
  #iterate through list of properties, calculating autocorrelation values for the sequence, append to results dict
133
133
  for key in aai_properties:
134
- temp = 0
135
134
  for i in range(1, lag+1):
135
+ temp = 0 # reset per lag
136
136
  for j in range(len(sequence)-i):
137
- temp = temp + aai_properties[key][sequence[j]] * aai_properties[key][sequence[j+1]]
137
+ temp = temp + aai_properties[key][sequence[j]] * aai_properties[key][sequence[j+i]]
138
138
  if (len(sequence) - i == 0):
139
139
  moreaubroto_autocorrelation["MBAuto_" + key + "_" + str(i)] = round(
140
140
  temp / (len(sequence)), 3)
@@ -362,7 +362,7 @@ def geary_autocorrelation(sequence: str, lag: int = 30,
362
362
 
363
363
  def _std(array: list[float], ddof: int = 1) -> float:
364
364
  """
365
- Calculate the standard deviation of the array data, with associated means Delta Degrees
365
+ Calculate the standard deviation of the array data, with associated means Delta Degrees
366
366
  of Freedom (ddof).
367
367
 
368
368
  Parameters
@@ -377,5 +377,5 @@ def _std(array: list[float], ddof: int = 1) -> float:
377
377
  :result: np.array
378
378
  input array after standard deviation transformation.
379
379
  """
380
- return math.sqrt(sum([math.pow(i - sum(array) / len(array), 2) for i in array])
381
- / (len(array) - ddof))
380
+ mean = sum(array) / len(array)
381
+ return math.sqrt(sum(math.pow(v - mean, 2) for v in array) / (len(array) - ddof))
@@ -205,9 +205,10 @@ def tripeptide_composition(sequence: str) -> pd.DataFrame:
205
205
  =======
206
206
  :tripeptide_composition_df: pd.DataFrame
207
207
  pandas DataFrame of tripeptide composition for protein sequence. Dataframe will
208
- be of the shape 1 x 8000, where 8000 is the number of features calculated
209
- from the descriptor (20^3 for the 20 canonical amino acids) and 1 is the
210
- input sequence.
208
+ be of the shape 1 x 8000, where 8000 is the number of features calculated
209
+ from the descriptor (20^3 for the 20 canonical amino acids) and 1 is the
210
+ input sequence. Values are percentages (0–100), consistent with
211
+ dipeptide_composition.
211
212
  """
212
213
  #validate input protein sequence
213
214
  sequence = _validate_sequence(sequence)
@@ -221,9 +222,10 @@ def tripeptide_composition(sequence: str) -> pd.DataFrame:
221
222
  for k in amino_acids:
222
223
  tripeptides.append(i + j + k)
223
224
 
224
- #get frequency of each tripeptide in the sequence
225
+ #get fractional frequency of each tripeptide (percentage), consistent with dipeptide_composition
226
+ total_tripeptides = len(sequence) - 2
225
227
  for i in tripeptides:
226
- tripeptide_composition[i] = len(re.findall(i, sequence))
228
+ tripeptide_composition[i] = round(sequence.count(i) / total_tripeptides * 100, 3) if total_tripeptides > 0 else 0.0
227
229
 
228
230
  #transform values and columns to DataFrame
229
231
  tripeptide_composition_df = pd.DataFrame([list(tripeptide_composition.values())],
@@ -1185,30 +1187,23 @@ def pseudo_amino_acid_composition(sequence: str, lamda: int = 30, weight: float
1185
1187
  raise ValueError(f"Accession number not found in aaindex1: {prop}.")
1186
1188
  property_values.append(aaindex1[prop].values)
1187
1189
 
1188
- right_part = 0.0
1189
1190
  psuedo_aac = {}
1190
- rightpart = []
1191
-
1192
- #calculate correlation factor for protein sequence using propeties
1193
- for i in range(lamda):
1194
- right_part = right_part + sequence_order_correlation_factor(sequence, i+1, property_values)
1195
1191
 
1196
1192
  #calculate amino acid composition
1197
1193
  aa_comp = amino_acid_composition(sequence)
1198
-
1199
- #compute first 20 pseudo amino acid descriptor components based on property_values,
1200
- #append descriptor values to dict
1201
- temp = 1 + weight + right_part
1202
- for index, i in enumerate(amino_acids):
1203
- psuedo_aac["PAAC_" + str(index + 1)] = round(aa_comp[i].values[0] / temp, 3)
1204
1194
 
1205
- #calculate correlation factor for protein sequence using propeties
1206
- for i in range(lamda):
1207
- rightpart.append(sequence_order_correlation_factor(sequence, i + 1, property_values))
1195
+ #compute correlation factors once; reuse for both the first-20 and last-lamda features
1196
+ rightpart = [sequence_order_correlation_factor(sequence, i + 1, property_values)
1197
+ for i in range(lamda)]
1208
1198
 
1209
- #compute last 20+lambda pseudo amino acid descriptor components based on property_values,
1210
- #append descriptor values to dict
1199
+ #shared normalization denominator: 1 + w * Σθ_k (Chou 2001)
1211
1200
  temp = 1 + weight * sum(rightpart)
1201
+
1202
+ #compute first 20 pseudo amino acid descriptor components (weighted AAComp)
1203
+ for index, i in enumerate(amino_acids):
1204
+ psuedo_aac["PAAC_" + str(index + 1)] = round(aa_comp[i].values[0] / temp, 3)
1205
+
1206
+ #compute last lamda pseudo amino acid descriptor components (sequence-order correlations)
1212
1207
  for index in range(20, 20 + lamda):
1213
1208
  psuedo_aac["PAAC_" + str(index + 1)] = round(
1214
1209
  weight * rightpart[index - 20] / temp * 100, 3)
@@ -1293,25 +1288,21 @@ def normalize_property(properties: dict[str, float]) -> dict[str, float]:
1293
1288
  """
1294
1289
  Normalize physicochemical/structural property values using
1295
1290
  their mean and standard deviation.
1296
-
1291
+
1297
1292
  Parameters
1298
1293
  ==========
1299
1294
  :properties: dict
1300
- dictionary of amino acid values for a physicochemical property.
1301
-
1295
+ dictionary of amino acid values for a physicochemical property.
1296
+
1302
1297
  Returns
1303
1298
  =======
1304
1299
  :normalized_vals: dict
1305
1300
  dict of normalized property values.
1306
1301
  """
1307
- normalized_vals = {}
1308
-
1309
- #iterate over all property values, calculate their mean and standard dev
1310
- for i, j in properties.items():
1311
- mean = sum(properties.values()) / len(properties.values())
1312
- normalized_vals[i] = (j - mean) / _std(properties.values(), mean, ddof=0)
1313
-
1314
- return normalized_vals
1302
+ vals = list(properties.values())
1303
+ mean = sum(vals) / len(vals)
1304
+ std = _std(vals, mean, ddof=0)
1305
+ return {aa: (v - mean) / std for aa, v in properties.items()}
1315
1306
 
1316
1307
  def _std(prop: list[float], mean: float, ddof: int = 1) -> float:
1317
1308
  """
@@ -1339,7 +1330,7 @@ def _std(prop: list[float], mean: float, ddof: int = 1) -> float:
1339
1330
  ######################## Amphiphilic Pseudo Amino Acid Composition ########################
1340
1331
 
1341
1332
  def amphiphilic_pseudo_amino_acid_composition(sequence: str, lamda: int = 30, weight: float = 0.5,
1342
- properties: list[dict[str, float]] = [hydrophobicity_, hydrophilicity_]) -> pd.DataFrame:
1333
+ properties: list[dict[str, float]] | None = None) -> pd.DataFrame:
1343
1334
  """
1344
1335
  Amphiphillic pseudo amino acid composition (APAAComp) has the same form as the amino
1345
1336
  acid composition, but contains much more information that is related to the
@@ -1358,59 +1349,59 @@ def amphiphilic_pseudo_amino_acid_composition(sequence: str, lamda: int = 30, we
1358
1349
  rank of correlation. Number of calculable descriptors depends on lambda value.
1359
1350
  :weight: float
1360
1351
  weighting factor.
1361
- :properties: list (default=[hydrophobicity, hydrophilicity])
1362
- list of dicts of physicochemical/structural property values for amino acids.
1352
+ :properties: list[dict] | None (default=None → [hydrophobicity_, hydrophilicity_])
1353
+ list of property dicts mapping each amino acid to a float value.
1354
+ When None the default hydrophobicity and hydrophilicity scales are used.
1363
1355
 
1364
1356
  Returns
1365
- =======
1357
+ =======
1366
1358
  :amp_pseudo_amino_acid_composition_df: pd.Dataframe
1367
- output dataframe of calculated amphiphilic pseudo amino acid composition
1368
- descriptors for input sequence. Dataframe will be of the shape 1 x N, where
1369
- N is the number of features calculated from the descriptor (20 + 2*lambda)
1370
- and 1 is the input sequence. By default, the shape will be 1 x 80
1371
- (using default lamda=30).
1359
+ output dataframe of calculated amphiphilic pseudo amino acid composition
1360
+ descriptors for input sequence. Dataframe will be of the shape 1 x N, where
1361
+ N is 20 + n_props * lamda (n_props = number of properties supplied).
1362
+ With the default two properties and lamda=30 the shape is 1 x 80.
1372
1363
  """
1364
+ if properties is None:
1365
+ properties = [hydrophobicity_, hydrophilicity_]
1366
+
1373
1367
  #validate input protein sequence
1374
1368
  sequence = _validate_sequence(sequence)
1375
1369
 
1376
1370
  #set lamda to its default value of 30 if <0, or > sequence len or not an int
1377
1371
  if ((lamda < 0) or (lamda > len(sequence)) or not isinstance(lamda, int)):
1378
- lamda = 30
1372
+ lamda = 30
1379
1373
 
1380
1374
  #validate weight, set default weight of 0.5 if invalid value input
1381
1375
  if ((weight < 0) or not (isinstance(weight, (float, int)))):
1382
1376
  weight = 0.5
1383
1377
 
1384
- #cast properties to list
1385
- if not (isinstance(properties, list)):
1378
+ #cast properties to list
1379
+ if not isinstance(properties, list):
1386
1380
  properties = [properties]
1387
1381
 
1382
+ n_props = len(properties)
1388
1383
  amp_pseudo_amino_acid_composition = {}
1389
1384
 
1390
- #calculate correlation factor in protein sequence
1391
- rightpart = 0.0
1385
+ #compute all per-lag correlation factors in a single pass:
1386
+ #rightpart = [p0_lag1, p1_lag1, p0_lag2, p1_lag2, ..., p{n-1}_lag{lamda}]
1387
+ rightpart: list[float] = []
1392
1388
  for i in range(lamda):
1393
- rightpart = rightpart + sum(
1394
- amphiphilic_sequence_order_correlation_factor(sequence, k=i + 1))
1395
-
1389
+ rightpart.extend(
1390
+ amphiphilic_sequence_order_correlation_factor(sequence, k=i + 1, properties=properties))
1391
+
1396
1392
  #calculate amino acid composition
1397
1393
  aa_composition = amino_acid_composition(sequence)
1398
1394
 
1399
- #compute first 20 pseudo amino acid descriptor components based on properties
1400
- temp = 1 + weight * rightpart
1401
- for index, i in enumerate(amino_acids):
1402
- amp_pseudo_amino_acid_composition["APAAC_" + str(index + 1)] = round(aa_composition[i].values[0] / temp, 3)
1395
+ #shared normalization denominator: 1 + w * Σ(all correlation factors)
1396
+ temp = 1 + weight * sum(rightpart)
1403
1397
 
1404
- #calculate correlation factor in protein sequence
1405
- rightpart = []
1406
- for i in range(lamda):
1407
- temp = amphiphilic_sequence_order_correlation_factor(sequence, k=i + 1)
1408
- rightpart.append(temp[0])
1409
- rightpart.append(temp[1])
1398
+ #compute first 20 pseudo amino acid descriptor components (weighted AAComp)
1399
+ for index, i in enumerate(amino_acids):
1400
+ amp_pseudo_amino_acid_composition["APAAC_" + str(index + 1)] = round(
1401
+ aa_composition[i].values[0] / temp, 3)
1410
1402
 
1411
- #compute remaining (20 + 2 * lamda) amphiphillic composition values
1412
- temp = 1 + weight * sum(rightpart)
1413
- for index in range(20, 20 + 2 * lamda):
1403
+ #compute remaining n_props * lamda amphiphilic composition values
1404
+ for index in range(20, 20 + n_props * lamda):
1414
1405
  amp_pseudo_amino_acid_composition["APAAC_" + str(index + 1)] = round(
1415
1406
  weight * rightpart[index - 20] / temp * 100, 3)
1416
1407
 
@@ -1420,9 +1411,10 @@ def amphiphilic_pseudo_amino_acid_composition(sequence: str, lamda: int = 30, we
1420
1411
 
1421
1412
  return amp_pseudo_amino_acid_composition_df
1422
1413
 
1423
- def amphiphilic_sequence_order_correlation_factor(sequence: str, k: int = 1) -> list[float]:
1424
- """
1425
- Calculate Amphipillic sequence order correlation factor for sequence
1414
+ def amphiphilic_sequence_order_correlation_factor(sequence: str, k: int = 1,
1415
+ properties: list[dict[str, float]] | None = None) -> list[float]:
1416
+ """
1417
+ Calculate Amphiphilic sequence order correlation factor for sequence
1426
1418
  with gap=k.
1427
1419
 
1428
1420
  Parameters
@@ -1431,34 +1423,36 @@ def amphiphilic_sequence_order_correlation_factor(sequence: str, k: int = 1) ->
1431
1423
  protein sequence.
1432
1424
  :k: int (default=1)
1433
1425
  gap between amino acids in the sequence.
1426
+ :properties: list[dict] | None (default=None → [hydrophobicity_, hydrophilicity_])
1427
+ list of property dicts. Pre-normalized once per call.
1434
1428
 
1435
1429
  Returns
1436
1430
  =======
1437
- :correlation_factor: list
1438
- list of correlation factors for both hydrophobicity and hydrophillicty.
1431
+ :correlation_factor: list[float]
1432
+ one correlation factor per property.
1439
1433
  """
1440
- correlation_factor_hydrophobicity = []
1441
- correlation_factor_hydrophilicity = []
1434
+ if properties is None:
1435
+ properties = [hydrophobicity_, hydrophilicity_]
1442
1436
 
1443
- #iterate over sequence, calculating correlation factors using both properties
1444
- for i in range(len(sequence) - k):
1445
- aa1 = sequence[i]
1446
- aa2 = sequence[i + k]
1447
- corelation_function = amphiphilic_correllation_function(aa1, aa2)
1448
- correlation_factor_hydrophobicity.append(corelation_function[0])
1449
- correlation_factor_hydrophilicity.append(corelation_function[1])
1437
+ #pre-normalize all properties once rather than inside every pair evaluation
1438
+ normalized_props = [normalize_property(prop) for prop in properties]
1439
+ n_props = len(properties)
1440
+ accumulators: list[list[float]] = [[] for _ in range(n_props)]
1450
1441
 
1451
- #append hydrophobicity and hydrophilicity values to array
1452
- correlation_factor = []
1453
- correlation_factor.append(round(sum(correlation_factor_hydrophobicity) / (len(sequence) - k), 3))
1454
- correlation_factor.append(round(sum(correlation_factor_hydrophilicity) / (len(sequence) - k), 3))
1442
+ #iterate over sequence pairs with the given gap k
1443
+ for i in range(len(sequence) - k):
1444
+ aa1, aa2 = sequence[i], sequence[i + k]
1445
+ for p_idx, norm in enumerate(normalized_props):
1446
+ accumulators[p_idx].append(round(norm[aa1] * norm[aa2], 3))
1455
1447
 
1456
- return correlation_factor
1448
+ denom = len(sequence) - k
1449
+ return [round(sum(acc) / denom, 3) for acc in accumulators]
1457
1450
 
1458
- def amphiphilic_correllation_function(Ri: str = "S", Rj: str = "D") -> tuple[float, float]:
1451
+ def amphiphilic_correllation_function(Ri: str = "S", Rj: str = "D",
1452
+ normalized_props: list[dict[str, float]] | None = None) -> list[float]:
1459
1453
  """
1460
- Calculate correlation value based on hydrophobicity and hydrophillicity
1461
- property values for input amino acids Ri and Rj in sequence.
1454
+ Calculate correlation value based on physicochemical property values
1455
+ for input amino acids Ri and Rj.
1462
1456
 
1463
1457
  Parameters
1464
1458
  ==========
@@ -1466,18 +1460,16 @@ def amphiphilic_correllation_function(Ri: str = "S", Rj: str = "D") -> tuple[flo
1466
1460
  1st amino acid.
1467
1461
  :Rj: str
1468
1462
  2nd amino acid.
1463
+ :normalized_props: list[dict] | None
1464
+ pre-normalized property dicts. Defaults to normalized hydrophobicity
1465
+ and hydrophilicity when None.
1469
1466
 
1470
1467
  Returns
1471
1468
  =======
1472
- :theta1, theta2: float
1473
- correlation values for input property values.
1469
+ :thetas: list[float]
1470
+ one correlation value per property.
1474
1471
  """
1475
- #normalize properties
1476
- _hydrophobicity = normalize_property(hydrophobicity_)
1477
- _hydrophilicity = normalize_property(hydrophilicity_)
1478
-
1479
- #calculate correlation using hydrophobicity & hydrophilicty properties
1480
- theta1 = round(_hydrophobicity[Ri] * _hydrophobicity[Rj], 3)
1481
- theta2 = round(_hydrophilicity[Ri] * _hydrophilicity[Rj], 3)
1482
-
1483
- return theta1, theta2
1472
+ if normalized_props is None:
1473
+ normalized_props = [normalize_property(hydrophobicity_),
1474
+ normalize_property(hydrophilicity_)]
1475
+ return [round(prop[Ri] * prop[Rj], 3) for prop in normalized_props]
@@ -285,6 +285,7 @@ def encode_dataset(
285
285
  output_file: str | None = "descriptors.csv",
286
286
  save_each_sequence: bool = True,
287
287
  strip_gaps: bool = False,
288
+ verbose: bool = True,
288
289
  ) -> pd.DataFrame:
289
290
  """
290
291
  Compute all available descriptors for every sequence in a dataset and
@@ -334,6 +335,11 @@ def encode_dataset(
334
335
  amino acid. When ``False`` (default), a ``-`` in any sequence raises
335
336
  a ``ValueError``.
336
337
 
338
+ :verbose: bool (default=True)
339
+ When ``True``, print a status line for each sequence as it is encoded
340
+ (``"Encoding sequence N/total: id"``). Set to ``False`` to suppress
341
+ all stdout output from this function.
342
+
337
343
  Returns
338
344
  =======
339
345
  :result: pd.DataFrame
@@ -400,7 +406,8 @@ def encode_dataset(
400
406
  total = len(seqs)
401
407
 
402
408
  for idx, (seq_id, seq) in enumerate(zip(ids, seqs), start=1):
403
- print(f"Encoding sequence {idx}/{total}: {seq_id}")
409
+ if verbose:
410
+ print(f"Encoding sequence {idx}/{total}: {seq_id}")
404
411
  if strip_gaps:
405
412
  seq = seq.replace("-", "")
406
413
  elif "-" in seq:
@@ -6,10 +6,10 @@ from __future__ import annotations
6
6
 
7
7
  import os
8
8
  import json
9
+ import functools
9
10
  from difflib import get_close_matches
10
11
  import pandas as pd
11
12
  import math
12
- from json import JSONDecodeError
13
13
  import sys
14
14
  from . import composition
15
15
 
@@ -33,6 +33,19 @@ References
33
33
  #list of amino acids
34
34
  from ._constants import amino_acids, _validate_sequence
35
35
 
36
+ @functools.lru_cache(maxsize=2)
37
+ def _load_distance_matrix(name: str) -> dict:
38
+ """Load and cache a physicochemical distance matrix JSON by name."""
39
+ path = os.path.join(
40
+ os.path.dirname(os.path.abspath(sys.modules['protpy'].__file__)),
41
+ "data", f"{name}-physicochemical-distance-matrix.json"
42
+ )
43
+ try:
44
+ with open(path) as f:
45
+ return json.load(f)
46
+ except (OSError, json.JSONDecodeError):
47
+ raise ValueError(f"Error parsing distance matrix file: {path}.")
48
+
36
49
  ############################# Sequence Order Coupling Number ##############################
37
50
 
38
51
  def sequence_order_coupling_number_(sequence: str, d: int = 1, distance_matrix: str = "schneider-wrede") -> float:
@@ -73,24 +86,17 @@ def sequence_order_coupling_number_(sequence: str, d: int = 1, distance_matrix:
73
86
  raise ValueError(f"Could not find a match for the input distance matrix {distance_matrix} in"
74
87
  f" list of available valid distance matrices:\n{['schneider-wrede', 'grantham']}.")
75
88
 
76
- #get full path to selected distance matric
77
- distance_matrix = os.path.join(os.path.dirname(os.path.abspath(sys.modules['protpy'].__file__)), "data", distance_matrix + "-physicochemical-distance-matrix.json")
78
-
79
- #open distance matrix json if present
80
- try:
81
- with open(distance_matrix, "r") as f:
82
- distance_matrix = json.load(f)
83
- except (OSError, json.JSONDecodeError):
84
- raise JSONDecodeError(f'Error parsing distance matrix JSON file: {distance_matrix}.')
89
+ #load (or retrieve cached) distance matrix
90
+ distance_matrix_data = _load_distance_matrix(distance_matrix)
85
91
 
86
92
  tau = 0.0
87
93
 
88
94
  #iterate over length of sequence minus gap, incrementing the SOCN at each iteration using
89
- #the values from the distance matrix with the current and next amino acid, round to 2 dp
95
+ #the values from the distance matrix with the current and next amino acid
90
96
  for aa in range(len(sequence) - d):
91
97
  current_aa = sequence[aa]
92
98
  next_aa = sequence[aa + d]
93
- tau = tau + math.pow(distance_matrix[current_aa + next_aa], 2)
99
+ tau = tau + math.pow(distance_matrix_data[current_aa + next_aa], 2)
94
100
 
95
101
  return round(tau, 3)
96
102
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: protpy
3
- Version: 1.4.1
3
+ Version: 1.4.2
4
4
  Summary: Python package for generating various biochemical, physicochemical and structural descriptors/features of protein sequences.
5
5
  Author-email: AJ McKenna <amckenna41@qub.ac.uk>
6
6
  Maintainer-email: AJ McKenna <amckenna41@qub.ac.uk>
@@ -32,9 +32,9 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
32
32
  Requires-Python: >=3.9
33
33
  Description-Content-Type: text/markdown
34
34
  License-File: LICENSE
35
- Requires-Dist: aaindex>=1.2.0
36
- Requires-Dist: numpy>=2.4.4
37
- Requires-Dist: pandas>=3.0.2
35
+ Requires-Dist: aaindex>=1.3.1
36
+ Requires-Dist: numpy>=2.0.0
37
+ Requires-Dist: pandas>=2.0.2
38
38
  Requires-Dist: tqdm>=4.0.0
39
39
  Requires-Dist: varname>=0.15.1
40
40
  Provides-Extra: test
@@ -1,6 +1,6 @@
1
- aaindex>=1.2.0
2
- numpy>=2.4.4
3
- pandas>=3.0.2
1
+ aaindex>=1.3.1
2
+ numpy>=2.0.0
3
+ pandas>=2.0.2
4
4
  tqdm>=4.0.0
5
5
  varname>=0.15.1
6
6
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "protpy"
7
- version = "1.4.1"
7
+ version = "1.4.2"
8
8
  description = "Python package for generating various biochemical, physicochemical and structural descriptors/features of protein sequences."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -50,9 +50,9 @@ classifiers = [
50
50
  ]
51
51
  requires-python = ">=3.9"
52
52
  dependencies = [
53
- "aaindex>=1.2.0",
54
- "numpy>=2.4.4",
55
- "pandas>=3.0.2",
53
+ "aaindex>=1.3.1",
54
+ "numpy>=2.0.0",
55
+ "pandas>=2.0.2",
56
56
  "tqdm>=4.0.0",
57
57
  "varname>=0.15.1",
58
58
  ]
@@ -148,7 +148,7 @@ class ProtpyAutocorrelationTests(unittest.TestCase):
148
148
  properties=["CIDH920105"], normalize=False)
149
149
  moreaubroto_nf_expected = pd.DataFrame(columns=["MBAuto_CIDH920105_1", "MBAuto_CIDH920105_2",
150
150
  "MBAuto_CIDH920105_3", "MBAuto_CIDH920105_4", "MBAuto_CIDH920105_5"])
151
- moreaubroto_nf_expected.loc[0] = [-0.054, -0.107, -0.161, -0.215, -0.269]
151
+ moreaubroto_nf_expected.loc[0] = [-0.054, -0.074, -0.038, -0.018, -0.05]
152
152
  self.assertIsInstance(moreaubroto_nf_seq1, pd.DataFrame,
153
153
  'Expected output to be of type DataFrame, got {}.'.format(type(moreaubroto_nf_seq1)))
154
154
  self.assertEqual(moreaubroto_nf_seq1.shape, (1, lag),
@@ -345,7 +345,7 @@ class ProtpyCompositionTests(unittest.TestCase):
345
345
  tripeptide_comp_seq1 = protpy.tripeptide_composition(self.protein_seq1)
346
346
  #testing first 10 columns
347
347
  tripeptide_comp_seq1_expected_values = pd.DataFrame(columns=["AAA", "AAC", "AAD", "AAE", "AAF", "AAG", "AAH", "AAI", "AAK", "AAL"])
348
- tripeptide_comp_seq1_expected_values.loc[0] = [1, 0, 0, 2, 0, 0, 0, 0, 0, 2]
348
+ tripeptide_comp_seq1_expected_values.loc[0] = [0.08, 0.0, 0.0, 0.16, 0.0, 0.0, 0.0, 0.0, 0.0, 0.16]
349
349
 
350
350
  self.assertIsInstance(tripeptide_comp_seq1, pd.DataFrame,
351
351
  'Expected output to be of type DataFrame, got {}.'.format(type(tripeptide_comp_seq1)))
@@ -365,13 +365,13 @@ class ProtpyCompositionTests(unittest.TestCase):
365
365
  "Amino acid {} not found in list of amino acids:\n{}.".format(col[2], self.amino_acids))
366
366
  self.assertTrue(tripeptide_comp_seq1.any().isnull().sum()==0,
367
367
  'Expected output to contain no null values.')
368
- self.assertTrue(all(col == np.int64 for col in list(tripeptide_comp_seq1.dtypes)),
368
+ self.assertTrue(all(col == np.float64 for col in list(tripeptide_comp_seq1.dtypes)),
369
369
  "Expected output values to be of datatype np.float64, got {}.".format(list(tripeptide_comp_seq1.dtypes)))
370
370
  #2.)
371
371
  tripeptide_comp_seq2 = protpy.tripeptide_composition(self.protein_seq2)
372
372
  #testing first 10 columns
373
373
  tripeptide_comp_seq2_expected_values = pd.DataFrame(columns=["AAA", "AAC", "AAD", "AAE", "AAF", "AAG", "AAH", "AAI", "AAK", "AAL"])
374
- tripeptide_comp_seq2_expected_values.loc[0] = [0, 0, 1, 1, 0, 0, 0, 0, 0, 0]
374
+ tripeptide_comp_seq2_expected_values.loc[0] = [0.0, 0.0, 0.238, 0.238, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
375
375
 
376
376
  self.assertIsInstance(tripeptide_comp_seq2, pd.DataFrame,
377
377
  'Expected output to be of type DataFrame, got {}.'.format(type(tripeptide_comp_seq2)))
@@ -391,13 +391,13 @@ class ProtpyCompositionTests(unittest.TestCase):
391
391
  "Amino acid {} not found in list of amino acids:\n{}.".format(col[2], self.amino_acids))
392
392
  self.assertTrue(tripeptide_comp_seq2.any().isnull().sum()==0,
393
393
  'Expected output to contain no null values.')
394
- self.assertTrue(all(col == np.int64 for col in list(tripeptide_comp_seq2.dtypes)),
394
+ self.assertTrue(all(col == np.float64 for col in list(tripeptide_comp_seq2.dtypes)),
395
395
  "Expected output values to be of datatype np.float64, got {}.".format(list(tripeptide_comp_seq2.dtypes)))
396
396
  #3.)
397
397
  tripeptide_comp_seq3 = protpy.tripeptide_composition(self.protein_seq3)
398
398
  #testing first 10 columns
399
399
  tripeptide_comp_seq3_expected_values = pd.DataFrame(columns=["AAA", "AAC", "AAD", "AAE", "AAF", "AAG", "AAH", "AAI", "AAK", "AAL"])
400
- tripeptide_comp_seq3_expected_values.loc[0] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
400
+ tripeptide_comp_seq3_expected_values.loc[0] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
401
401
 
402
402
  self.assertIsInstance(tripeptide_comp_seq3, pd.DataFrame,
403
403
  'Expected output to be of type DataFrame, got {}.'.format(type(tripeptide_comp_seq3)))
@@ -417,13 +417,13 @@ class ProtpyCompositionTests(unittest.TestCase):
417
417
  "Amino acid {} not found in list of amino acids:\n{}.".format(col[2], self.amino_acids))
418
418
  self.assertTrue(tripeptide_comp_seq3.any().isnull().sum()==0,
419
419
  'Expected output to contain no null values.')
420
- self.assertTrue(all(col == np.int64 for col in list(tripeptide_comp_seq3.dtypes)),
420
+ self.assertTrue(all(col == np.float64 for col in list(tripeptide_comp_seq3.dtypes)),
421
421
  "Expected output values to be of datatype np.float64, got {}.".format(list(tripeptide_comp_seq3.dtypes)))
422
422
  #4.)
423
423
  tripeptide_comp_seq4 = protpy.tripeptide_composition(self.protein_seq4)
424
424
  #testing first 10 columns
425
425
  tripeptide_comp_seq4_expected_values = pd.DataFrame(columns=["AAA", "AAC", "AAD", "AAE", "AAF", "AAG", "AAH", "AAI", "AAK", "AAL"])
426
- tripeptide_comp_seq4_expected_values.loc[0] = [0, 0, 1, 1, 0, 0, 0, 1, 0, 1]
426
+ tripeptide_comp_seq4_expected_values.loc[0] = [0.0, 0.0, 0.24, 0.24, 0.0, 0.0, 0.0, 0.24, 0.0, 0.24]
427
427
 
428
428
  self.assertIsInstance(tripeptide_comp_seq4, pd.DataFrame,
429
429
  'Expected output to be of type DataFrame, got {}.'.format(type(tripeptide_comp_seq4)))
@@ -443,7 +443,7 @@ class ProtpyCompositionTests(unittest.TestCase):
443
443
  "Amino acid {} not found in list of amino acids:\n{}.".format(col[2], self.amino_acids))
444
444
  self.assertTrue(tripeptide_comp_seq4.any().isnull().sum()==0,
445
445
  'Expected output to contain no null values.')
446
- self.assertTrue(all(col == np.int64 for col in list(tripeptide_comp_seq4.dtypes)),
446
+ self.assertTrue(all(col == np.float64 for col in list(tripeptide_comp_seq4.dtypes)),
447
447
  "Expected output values to be of datatype np.float64, got {}.".format(list(tripeptide_comp_seq4.dtypes)))
448
448
  #5.)
449
449
  invalid_seq5 = "ABCDEF"
@@ -469,7 +469,7 @@ class ProtpyCompositionTests(unittest.TestCase):
469
469
  pseudo_amino_acid_composition_seq1 = protpy.pseudo_amino_acid_composition(self.protein_seq1, weight=weight, lamda=lamda)
470
470
  #testing first 10 columns
471
471
  pseudo_amino_acid_composition_seq1_expected_values = pd.DataFrame(columns=["PAAC_1", "PAAC_2", "PAAC_3", "PAAC_4", "PAAC_5", "PAAC_6", "PAAC_7", "PAAC_8", "PAAC_9", "PAAC_10"])
472
- pseudo_amino_acid_composition_seq1_expected_values.loc[0] = [0.127, 0.059, 0.111, 0.064, 0.126, 0.12, 0.023, 0.118, 0.091, 0.15]
472
+ pseudo_amino_acid_composition_seq1_expected_values.loc[0] = [1.871, 0.869, 1.626, 0.936, 1.849, 1.76, 0.334, 1.737, 1.336, 2.205]
473
473
 
474
474
  self.assertIsInstance(pseudo_amino_acid_composition_seq1, pd.DataFrame,
475
475
  'Expected output to be of type DataFrame, got {}.'.format(type(pseudo_amino_acid_composition_seq1)))
@@ -489,7 +489,7 @@ class ProtpyCompositionTests(unittest.TestCase):
489
489
  pseudo_amino_acid_composition_seq2 = protpy.pseudo_amino_acid_composition(self.protein_seq2, weight=weight, lamda=lamda)
490
490
  #testing first 10 columns
491
491
  pseudo_amino_acid_composition_seq2_expected_values = pd.DataFrame(columns=["PAAC_1", "PAAC_2", "PAAC_3", "PAAC_4", "PAAC_5", "PAAC_6", "PAAC_7", "PAAC_8", "PAAC_9", "PAAC_10"])
492
- pseudo_amino_acid_composition_seq2_expected_values.loc[0] = [0.137, 0.0, 0.088, 0.056, 0.052, 0.181, 0.02, 0.044, 0.117, 0.105]
492
+ pseudo_amino_acid_composition_seq2_expected_values.loc[0] = [2.069, 0.0, 1.338, 0.852, 0.791, 2.738, 0.304, 0.669, 1.764, 1.582]
493
493
 
494
494
  self.assertIsInstance(pseudo_amino_acid_composition_seq2, pd.DataFrame,
495
495
  'Expected output to be of type DataFrame, got {}.'.format(type(pseudo_amino_acid_composition_seq2)))
@@ -509,7 +509,7 @@ class ProtpyCompositionTests(unittest.TestCase):
509
509
  pseudo_amino_acid_composition_seq3 = protpy.pseudo_amino_acid_composition(self.protein_seq3, weight=weight, lamda=lamda)
510
510
  #testing first 10 columns
511
511
  pseudo_amino_acid_composition_seq3_expected_values = pd.DataFrame(columns=["PAAC_1", "PAAC_2", "PAAC_3", "PAAC_4", "PAAC_5", "PAAC_6", "PAAC_7", "PAAC_8", "PAAC_9", "PAAC_10"])
512
- pseudo_amino_acid_composition_seq3_expected_values.loc[0] = [0.126, 0.095, 0.032, 0.095, 0.126, 0.063, 0.0, 0.095, 0.063, 0.441]
512
+ pseudo_amino_acid_composition_seq3_expected_values.loc[0] = [1.735, 1.301, 0.434, 1.301, 1.735, 0.868, 0.0, 1.301, 0.868, 6.072]
513
513
 
514
514
  self.assertIsInstance(pseudo_amino_acid_composition_seq3, pd.DataFrame,
515
515
  'Expected output to be of type DataFrame, got {}.'.format(type(pseudo_amino_acid_composition_seq3)))
@@ -529,7 +529,7 @@ class ProtpyCompositionTests(unittest.TestCase):
529
529
  pseudo_amino_acid_composition_seq4 = protpy.pseudo_amino_acid_composition(self.protein_seq4, weight=weight, lamda=lamda)
530
530
  #testing first 10 columns
531
531
  pseudo_amino_acid_composition_seq4_expected_values = pd.DataFrame(columns=["PAAC_1", "PAAC_2", "PAAC_3", "PAAC_4", "PAAC_5", "PAAC_6", "PAAC_7", "PAAC_8", "PAAC_9", "PAAC_10"])
532
- pseudo_amino_acid_composition_seq4_expected_values.loc[0] = [0.15, 0.0, 0.097, 0.048, 0.053, 0.174, 0.016, 0.057, 0.125, 0.109]
532
+ pseudo_amino_acid_composition_seq4_expected_values.loc[0] = [2.264, 0.0, 1.468, 0.734, 0.795, 2.631, 0.245, 0.856, 1.897, 1.652]
533
533
 
534
534
  self.assertIsInstance(pseudo_amino_acid_composition_seq4, pd.DataFrame,
535
535
  'Expected output to be of type DataFrame, got {}.'.format(type(pseudo_amino_acid_composition_seq4)))
@@ -22,8 +22,8 @@ class ProtpyTests(unittest.TestCase):
22
22
  """
23
23
  def test_protpy_metadata(self):
24
24
  """ Testing correct protpy version and metadata. """
25
- self.assertEqual(protpy.__version__, "1.4.1",
26
- "protpy version is not correct, expected version 1.4.1, got version {}".format(protpy.__version__))
25
+ self.assertEqual(protpy.__version__, "1.4.2",
26
+ "protpy version is not correct, expected version 1.4.2, got version {}".format(protpy.__version__))
27
27
  self.assertEqual(protpy.__name__, "protpy",
28
28
  "protpy software name is not correct, expected protpy, got {}.".format(protpy.__name__))
29
29
  self.assertEqual(protpy.__author__, "AJ McKenna: https://github.com/amckenna41",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes