Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cdisc_rules_engine/check_operators/dataframe_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ def equals_string_part(self, other_value):
column with a regex
"""
target = other_value.get("target")
type_insensitive = other_value.get("type_insensitive", False)
comparator = other_value.get("comparator")
regex = other_value.get("regex")
value_is_literal: bool = other_value.get("value_is_literal", False)
Expand All @@ -821,7 +822,11 @@ def equals_string_part(self, other_value):
self.value[parsed_id] = parsed_data
return self.value.apply(
lambda row: self._check_equality(
row, target, parsed_id, value_is_literal=False
row,
target,
parsed_id,
value_is_literal=False,
type_insensitive=type_insensitive,
),
axis=1,
)
Expand Down
10 changes: 8 additions & 2 deletions resources/schema/rule-merged/Operator.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@
"properties": {
"operator": {
"const": "does_not_equal_string_part",
"markdownDescription": "\nComplement of `equals_string_part`\n"
"markdownDescription": "\nComplement of `equals_string_part`. Also has the optional parameter 'type_insensitive'.\n"
},
"type_insensitive": {
"type": "boolean"
}
},
"required": ["operator", "value", "regex"],
Expand Down Expand Up @@ -223,7 +226,10 @@
"properties": {
"operator": {
"const": "equals_string_part",
"markdownDescription": "\nChecks that the values in the target column equal the result of parsing the value in the comparison column with a regex\n\n> RDOMAIN equals characters 5 and 6 of SUPP dataset name\n\n```yaml\n- name: RDOMAIN\n operator: equals_string_part\n value: dataset_name\n regex: \".{4}(..).*\"\n```\n"
"markdownDescription": "\nChecks that the values in the target column equal the result of parsing the value in the comparison column with a regex\nHas optional parameter:\n\n- 'type_insensitive' when true, both values are converted to strings before comparison to handle type mismatches between string and numeric data. NOTE: all leading and trailing zeroes will be removed in both strings and floats.\n\n> RDOMAIN equals characters 5 and 6 of SUPP dataset name\n\n```yaml\n- name: RDOMAIN\n operator: equals_string_part\n type_insensitive: true\n value: dataset_name\n regex: \".{4}(..).*\"\n```\n"
},
"type_insensitive": {
"type": "boolean"
}
},
"required": ["operator", "value", "regex"],
Expand Down
10 changes: 8 additions & 2 deletions resources/schema/rule/Operator.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@
"type": "object"
},
{
"properties": { "operator": { "const": "does_not_equal_string_part" } },
"properties": {
"operator": { "const": "does_not_equal_string_part" },
"type_insensitive": { "type": "boolean" }
},
"required": ["operator", "value", "regex"],
"type": "object"
},
Expand Down Expand Up @@ -120,7 +123,10 @@
"type": "object"
},
{
"properties": { "operator": { "const": "equals_string_part" } },
"properties": {
"operator": { "const": "equals_string_part" },
"type_insensitive": { "type": "boolean" }
},
"required": ["operator", "value", "regex"],
"type": "object"
},
Expand Down
6 changes: 5 additions & 1 deletion resources/schema/rule/Operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,21 @@ Text-based operations including regex pattern matching, substring operations, pr

### does_not_equal_string_part

Complement of `equals_string_part`
Complement of `equals_string_part`. Also has the optional parameter 'type_insensitive'.

### equals_string_part

Checks that the values in the target column equal the result of parsing the value in the comparison column with a regex
Has optional parameter:

- 'type_insensitive' when true, both values are converted to strings before comparison to handle type mismatches between string and numeric data. NOTE: all leading and trailing zeroes will be removed in both strings and floats.

> RDOMAIN equals characters 5 and 6 of SUPP dataset name

```yaml
- name: RDOMAIN
operator: equals_string_part
type_insensitive: true
value: dataset_name
regex: ".{4}(..).*"
```
Expand Down
199 changes: 199 additions & 0 deletions tests/unit/test_check_operators/test_string_comparison.py
Comment thread
aniemes marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
DaskDataset,
[True, True, False],
),
(
{"VAR2": ["<40"], "target": [40]},
"VAR2",
".(.*)",
PandasDataset,
[False],
),
(
{"VAR2": ["<40"], "target": [40]},
"VAR2",
".(.*)",
DaskDataset,
[False],
),
],
)
def test_equals_string_part(data, comparator, regex, dataset_type, expected_result):
Expand All @@ -33,6 +47,191 @@ def test_equals_string_part(data, comparator, regex, dataset_type, expected_resu
assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,operator,regex,dataset_type,expected_result",
[
(
{"VAR2": [">=40", "<=50"], "target": [40, 50]},
"VAR2",
"equals_string_part",
".{2}(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": [">=40", "<=50"], "target": [40, 50]},
"VAR2",
"equals_string_part",
".{2}(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">=040", "<=050"], "target": [40, 50]},
"VAR2",
"equals_string_part",
".{2}(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": [">=040", "<=050"], "target": [40, 50]},
"VAR2",
"equals_string_part",
".{2}(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">=42 ", "<=55 "], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".{2}(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": [">=42 ", "<=55 "], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".{2}(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">40", "<50"], "target": [40, 50]},
"VAR2",
"equals_string_part",
".(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">45", "<52"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": [">45", "<52"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">40", "<50"], "target": [40.0, 50.0]},
"VAR2",
"equals_string_part",
".(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": [">45", "<52"], "target": [40.0, 50.0]},
"VAR2",
"does_not_equal_string_part",
".(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": [">40", "<50"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".(.*)",
PandasDataset,
[False, False],
),
(
{"VAR2": [">40", "<50"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
".(.*)",
DaskDataset,
[False, False],
),
(
{"VAR2": ["0040", "0050"], "target": [40, 50]},
"VAR2",
"equals_string_part",
"(.*)",
PandasDataset,
[True, True],
),
(
{"VAR2": ["0040", "0050"], "target": [40, 50]},
"VAR2",
"equals_string_part",
"(.*)",
DaskDataset,
[True, True],
),
(
{"VAR2": ["40", "50"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
"^[A-Za-z]+$",
PandasDataset,
[True, True],
),
(
{"VAR2": ["40", "50"], "target": [40, 50]},
"VAR2",
"does_not_equal_string_part",
"^[A-Za-z]+$",
DaskDataset,
[True, True],
),
(
{"VAR2": ["", ""], "target": [None, None]},
"VAR2",
"equals_string_part",
"(.*)",
PandasDataset,
[False, False],
),
(
{"VAR2": ["", ""], "target": [None, None]},
"VAR2",
"equals_string_part",
"(.*)",
DaskDataset,
[False, False],
),
],
)
def test_equals_string_part_type_insensitive(
data, comparator, operator, regex, dataset_type, expected_result
):
df = dataset_type.from_dict(data)
dataframe_type = DataframeType({"value": df})

if operator == "equals_string_part":
result = dataframe_type.equals_string_part(
{
"target": "target",
"comparator": comparator,
"regex": regex,
"type_insensitive": True,
}
)
else:
result = dataframe_type.does_not_equal_string_part(
{
"target": "target",
"comparator": comparator,
"regex": regex,
"type_insensitive": True,
}
)

assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,regex,dataset_type,value_is_literal,expected_result",
[
Expand Down
Loading