Skip to content

Commit 0ed761d

Browse files
committed
add p separators for annotations
Signed-off-by: Florian Charlier <477844+trevismd@users.noreply.github.com>
1 parent d5c547d commit 0ed761d

File tree

6 files changed

+122
-39
lines changed

6 files changed

+122
-39
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
## v0.7
2+
### v0.7.2
3+
### Features
4+
- Add option to capitalize "p" in annotations
5+
(PR [#172](https://github.com/trevismd/statannotations/pull/172) by
6+
[Pentabyteman](https://github.com/Pentabyteman))
7+
- Add option to change spacing in annotations
8+
(PR [#173](https://github.com/trevismd/statannotations/pull/173))
9+
210
### v0.7.1
311
#### Fixes
412
- Fix minimum Python requirements in setup.py

coverage.svg

Lines changed: 2 additions & 2 deletions
Loading

statannotations/PValueFormat.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
from statannotations.stats.StatResult import StatResult
1+
from typing import Tuple, Union
22

33
from statannotations.format_annotations import pval_annotation_text, \
44
simple_text
5+
from statannotations.stats.StatResult import StatResult
56
from statannotations.utils import DEFAULT, check_valid_text_format, \
67
InvalidParametersError
78

89
CONFIGURABLE_PARAMETERS = [
910
'correction_format',
1011
'fontsize',
11-
'pvalue_format_string',
1212
'simple_format_string',
1313
'text_format',
14+
'pvalue_format_string',
1415
'pvalue_thresholds',
16+
'p_capitalized',
17+
'p_separators',
1518
'show_test_name',
16-
'p_capitalized'
1719
]
1820

1921

@@ -40,6 +42,7 @@ def __init__(self):
4042
self._correction_format = "{star} ({suffix})"
4143
self.show_test_name = True
4244
self.p_capitalized = False
45+
self._p_separators = (" ", " ")
4346

4447
def config(self, **parameters):
4548

@@ -69,6 +72,30 @@ def text_format(self, text_format):
6972
check_valid_text_format(text_format)
7073
self._text_format = text_format
7174

75+
@property
76+
def p_separators(self):
77+
return {
78+
(" ", " "): 'both',
79+
("", " "): 'after',
80+
("", ""): 'none',
81+
}.get(self._p_separators, self._p_separators)
82+
83+
@p_separators.setter
84+
def p_separators(self, p_separators: Union[bool, str, Tuple[str]] = True):
85+
"""
86+
:param p_separators:
87+
'both' (or True)(default), 'none' (or False), 'after',
88+
or tuple[bool] for before and after
89+
"""
90+
if isinstance(p_separators, tuple):
91+
self._p_separators = p_separators
92+
elif p_separators in {'none', False}:
93+
self._p_separators = ("", "")
94+
elif p_separators == 'after':
95+
self._p_separators = ("", " ")
96+
else:
97+
self._p_separators = (" ", " ")
98+
7299
def _get_pvalue_thresholds(self, pvalue_thresholds):
73100
if self._default_pvalue_thresholds:
74101
if self.text_format == "star":
@@ -179,10 +206,10 @@ def format_data(self, result):
179206
else "")
180207

181208
p_letter = "P" if self.p_capitalized else "p"
182-
183-
return ("{}{} = {}{}"
184-
.format('{}', p_letter, self.pvalue_format_string, '{}')
185-
.format(text, result.pvalue, result.significance_suffix))
209+
equals = f"{self._p_separators[0]}={self._p_separators[1]}"
210+
formatted_pvalue = self.pvalue_format_string.format(result.pvalue)
211+
full_pvalue = f"{formatted_pvalue}{result.significance_suffix}"
212+
return f"{text}{p_letter}{equals}{full_pvalue}"
186213

187214
elif self.text_format == 'star':
188215
was_list = False
@@ -200,10 +227,10 @@ def format_data(self, result):
200227

201228
return annotations[0]
202229

203-
# elif self.text_format == 'simple':
204230
else:
205-
return simple_text(result, self.simple_format_string,
206-
self.pvalue_thresholds, self.show_test_name, self.p_capitalized)
231+
return simple_text(
232+
result, self.simple_format_string, self.pvalue_thresholds,
233+
self.show_test_name, self.p_capitalized, self._p_separators)
207234

208235
def get_configuration(self):
209236
return {key: getattr(self, key) for key in CONFIGURABLE_PARAMETERS}

statannotations/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.7.1"
1+
__version__ = "0.7.2"

statannotations/format_annotations.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from statannotations.stats.StatResult import StatResult
2-
from typing import List
1+
from operator import itemgetter
2+
from typing import List, Tuple
3+
34
import numpy as np
45
import pandas as pd
5-
from operator import itemgetter
6+
7+
from statannotations.stats.StatResult import StatResult
68

79

810
def pval_annotation_text(result: List[StatResult],
@@ -32,32 +34,38 @@ def pval_annotation_text(result: List[StatResult],
3234
return [(star, res) for star, res in zip(x_annot, result)]
3335

3436

35-
def simple_text(result: StatResult, pvalue_format, pvalue_thresholds,
36-
short_test_name=True, p_capitalized=False) -> str:
37+
def simple_text(result: StatResult, pvalue_format: str,
38+
pvalue_thresholds: List[list],
39+
short_test_name: bool = True, p_capitalized: bool = False,
40+
separators: Tuple[str] = (" ", " ")) -> str:
3741
"""
3842
Generates simple text for test name and pvalue.
3943
4044
:param result: StatResult instance
4145
:param pvalue_format: format string for pvalue
4246
:param pvalue_thresholds: String to display per pvalue range
4347
:param short_test_name: whether to display the test (short) name
48+
:param p_capitalized: set True to show "P" instead of "p" in annotations
49+
:param separators: separators for before and after '=' or '≤'
4450
:returns: simple annotation
4551
4652
"""
47-
# Sort thresholds
4853
thresholds = sorted(pvalue_thresholds, key=lambda x: x[0])
4954

5055
text = (f"{result.test_short_name} "
5156
if short_test_name and result.test_short_name
5257
else "")
5358

5459
p_letter = "P" if p_capitalized else "p"
60+
p_equals = f"{p_letter}{separators[0]}={separators[1]}"
61+
p_lte = f"{p_letter}{separators[0]}{separators[1]}"
5562

5663
for threshold in thresholds:
5764
if result.pvalue < threshold[0]:
58-
pval_text = "{} ≤ {}".format(p_letter, threshold[1])
65+
pval_text = f"{p_lte}{threshold[1]}"
5966
break
6067
else:
61-
pval_text = "{} = {}".format(p_letter, pvalue_format).format(result.pvalue)
68+
formatted_pvalue = pvalue_format.format(result.pvalue)
69+
pval_text = f"{p_equals}{formatted_pvalue}"
6270

6371
return result.adjust(text + pval_text)

tests/test_pvalue_format.py

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -111,22 +111,26 @@ def test_print_pvalue_other(self):
111111

112112
def test_get_configuration(self):
113113
pvalue_format = PValueFormat()
114-
self.assertDictEqual(pvalue_format.get_configuration(),
115-
{'correction_format': '{star} ({suffix})',
116-
'fontsize': 'medium',
117-
'p_capitalized': False,
118-
'pvalue_format_string': '{:.3e}',
119-
'show_test_name': True,
120-
'simple_format_string': '{:.2f}',
121-
'text_format': 'star',
122-
'pvalue_thresholds': [
123-
[1e-4, "****"],
124-
[1e-3, "***"],
125-
[1e-2, "**"],
126-
[0.05, "*"],
127-
[1, "ns"]]
128-
}
129-
)
114+
self.assertDictEqual(
115+
pvalue_format.get_configuration(),
116+
{
117+
'correction_format': '{star} ({suffix})',
118+
'fontsize': 'medium',
119+
'p_capitalized': False,
120+
'p_separators': 'both',
121+
'pvalue_format_string': '{:.3e}',
122+
'pvalue_thresholds': [
123+
[1e-4, "****"],
124+
[1e-3, "***"],
125+
[1e-2, "**"],
126+
[0.05, "*"],
127+
[1, "ns"]
128+
],
129+
'show_test_name': True,
130+
'simple_format_string': '{:.2f}',
131+
'text_format': 'star',
132+
}
133+
)
130134

131135
def test_config_pvalue_thresholds(self):
132136
pvalue_format = PValueFormat()
@@ -138,7 +142,7 @@ def test_config_pvalue_thresholds(self):
138142
" ns: 5.00e-02 < p <= 1.00e+00\n"
139143
" <= 0.05: 1.00e-03 < p <= 5.00e-02\n"
140144
"<= 0.001: p <= 1.00e-03\n\n")
141-
145+
142146
def test_pvalue_simple_capitalized(self):
143147
self.annotator.configure(pvalue_format={"text_format": "simple",
144148
"p_capitalized": True})
@@ -153,4 +157,40 @@ def test_pvalue_full_capitalized(self):
153157
"pvalue_format_string": "{:.2f}"})
154158
annotations = self.annotator._get_results("auto", pvalues=self.pvalues)
155159
self.assertEqual(["P = 0.04", "P = 0.03", "P = 0.90"],
156-
[annotation.text for annotation in annotations])
160+
[annotation.text for annotation in annotations])
161+
162+
def test_pvalue_simple_separators(self):
163+
self.annotator.configure(pvalue_format={"text_format": "simple"})
164+
for separator, expected_results in [
165+
(None, ["p ≤ 0.05", "p ≤ 0.05", "p = 0.90"]),
166+
(True, ["p ≤ 0.05", "p ≤ 0.05", "p = 0.90"]),
167+
('both', ["p ≤ 0.05", "p ≤ 0.05", "p = 0.90"]),
168+
('none', ["p≤0.05", "p≤0.05", "p=0.90"]),
169+
('after', ["p≤ 0.05", "p≤ 0.05", "p= 0.90"]),
170+
]:
171+
with self.subTest(separator=separator):
172+
config = {"pvalue_format": {"p_separators": separator}}
173+
self.annotator.configure(**config)
174+
annotations = self.annotator._get_results("auto", pvalues=self.pvalues)
175+
self.assertEqual(expected_results, [a.text for a in annotations])
176+
177+
def test_pvalue_full_separators(self):
178+
self.annotator.configure(
179+
pvalue_format={
180+
"text_format": "full",
181+
"show_test_name": False,
182+
"pvalue_format_string": "{:.2f}"
183+
},
184+
)
185+
for separator, expected_results in [
186+
(None, ["p = 0.04", "p = 0.03", "p = 0.90"]),
187+
(True, ["p = 0.04", "p = 0.03", "p = 0.90"]),
188+
('both', ["p = 0.04", "p = 0.03", "p = 0.90"]),
189+
('none', ["p=0.04", "p=0.03", "p=0.90"]),
190+
('after', ["p= 0.04", "p= 0.03", "p= 0.90"]),
191+
]:
192+
with self.subTest(separator=separator):
193+
config = {"pvalue_format": {"p_separators": separator}}
194+
self.annotator.configure(**config)
195+
annotations = self.annotator._get_results("auto", pvalues=self.pvalues)
196+
self.assertEqual(expected_results, [a.text for a in annotations])

0 commit comments

Comments
 (0)