field-match 0.1.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.
- field_match-0.1.0/.DS_Store +0 -0
- field_match-0.1.0/.github/workflows/ci.yml +27 -0
- field_match-0.1.0/.gitignore +160 -0
- field_match-0.1.0/.idea/.gitignore +0 -0
- field_match-0.1.0/.idea/field-match.iml +12 -0
- field_match-0.1.0/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- field_match-0.1.0/.idea/misc.xml +4 -0
- field_match-0.1.0/.idea/modules.xml +8 -0
- field_match-0.1.0/.idea/vcs.xml +6 -0
- field_match-0.1.0/.idea/workspace.xml +181 -0
- field_match-0.1.0/LICENSE +21 -0
- field_match-0.1.0/PKG-INFO +66 -0
- field_match-0.1.0/README.md +53 -0
- field_match-0.1.0/examples/usage.py +41 -0
- field_match-0.1.0/pyproject.toml +24 -0
- field_match-0.1.0/requirements.txt +4 -0
- field_match-0.1.0/src/.DS_Store +0 -0
- field_match-0.1.0/src/field_match/.DS_Store +0 -0
- field_match-0.1.0/src/field_match/__init__.py +7 -0
- field_match-0.1.0/src/field_match/field_match.py +197 -0
- field_match-0.1.0/tests/__init__.py +0 -0
- field_match-0.1.0/tests/test_field_match.py +79 -0
|
Binary file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: continuous-integration
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v2
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v2
|
|
18
|
+
with:
|
|
19
|
+
python-version: 3.8
|
|
20
|
+
|
|
21
|
+
- name: Run tests
|
|
22
|
+
run: |
|
|
23
|
+
python -m venv venv
|
|
24
|
+
source venv/bin/activate
|
|
25
|
+
pip install --upgrade pip
|
|
26
|
+
pip install -r requirements.txt
|
|
27
|
+
python -m unittest discover -s tests
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
|
|
112
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
113
|
+
__pypackages__/
|
|
114
|
+
|
|
115
|
+
# Celery stuff
|
|
116
|
+
celerybeat-schedule
|
|
117
|
+
celerybeat.pid
|
|
118
|
+
|
|
119
|
+
# SageMath parsed files
|
|
120
|
+
*.sage.py
|
|
121
|
+
|
|
122
|
+
# Environments
|
|
123
|
+
.env
|
|
124
|
+
.venv
|
|
125
|
+
env/
|
|
126
|
+
venv/
|
|
127
|
+
ENV/
|
|
128
|
+
env.bak/
|
|
129
|
+
venv.bak/
|
|
130
|
+
|
|
131
|
+
# Spyder project settings
|
|
132
|
+
.spyderproject
|
|
133
|
+
.spyproject
|
|
134
|
+
|
|
135
|
+
# Rope project settings
|
|
136
|
+
.ropeproject
|
|
137
|
+
|
|
138
|
+
# mkdocs documentation
|
|
139
|
+
/site
|
|
140
|
+
|
|
141
|
+
# mypy
|
|
142
|
+
.mypy_cache/
|
|
143
|
+
.dmypy.json
|
|
144
|
+
dmypy.json
|
|
145
|
+
|
|
146
|
+
# Pyre type checker
|
|
147
|
+
.pyre/
|
|
148
|
+
|
|
149
|
+
# pytype static type analyzer
|
|
150
|
+
.pytype/
|
|
151
|
+
|
|
152
|
+
# Cython debug symbols
|
|
153
|
+
cython_debug/
|
|
154
|
+
|
|
155
|
+
# PyCharm
|
|
156
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
157
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
158
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
159
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
160
|
+
#.idea/
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
<component name="PyDocumentationSettings">
|
|
9
|
+
<option name="format" value="GOOGLE" />
|
|
10
|
+
<option name="myDocStringFormat" value="Google" />
|
|
11
|
+
</component>
|
|
12
|
+
</module>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/field-match.iml" filepath="$PROJECT_DIR$/.idea/field-match.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ChangeListManager">
|
|
4
|
+
<list default="true" id="eb64838e-f39e-4f99-94ef-8fb088601c45" name="Changes" comment="Update CI">
|
|
5
|
+
<change afterPath="$PROJECT_DIR$/.github/workflows/ci.yml" afterDir="false" />
|
|
6
|
+
<change afterPath="$PROJECT_DIR$/LICENSE" afterDir="false" />
|
|
7
|
+
<change afterPath="$PROJECT_DIR$/examples/usage.py" afterDir="false" />
|
|
8
|
+
<change afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
|
|
9
|
+
<change afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
|
10
|
+
<change afterPath="$PROJECT_DIR$/src/field_match/__init__.py" afterDir="false" />
|
|
11
|
+
<change afterPath="$PROJECT_DIR$/src/field_match/field_match.py" afterDir="false" />
|
|
12
|
+
<change afterPath="$PROJECT_DIR$/tests/__init__.py" afterDir="false" />
|
|
13
|
+
<change afterPath="$PROJECT_DIR$/tests/test_field_match.py" afterDir="false" />
|
|
14
|
+
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
15
|
+
</list>
|
|
16
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
17
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
18
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
19
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
20
|
+
</component>
|
|
21
|
+
<component name="FileTemplateManagerImpl">
|
|
22
|
+
<option name="RECENT_TEMPLATES">
|
|
23
|
+
<list>
|
|
24
|
+
<option value="Python Script" />
|
|
25
|
+
</list>
|
|
26
|
+
</option>
|
|
27
|
+
</component>
|
|
28
|
+
<component name="Git.Settings">
|
|
29
|
+
<option name="PREVIOUS_COMMIT_AUTHORS">
|
|
30
|
+
<list>
|
|
31
|
+
<option value="cstirry <christine.stirrat@gmail.com>" />
|
|
32
|
+
<option value="Christine Stirrat <christine.stirrat@gmail.com>" />
|
|
33
|
+
</list>
|
|
34
|
+
</option>
|
|
35
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
36
|
+
</component>
|
|
37
|
+
<component name="MarkdownSettingsMigration">
|
|
38
|
+
<option name="stateVersion" value="1" />
|
|
39
|
+
</component>
|
|
40
|
+
<component name="ProblemsViewState">
|
|
41
|
+
<option name="selectedTabId" value="ProjectErrors" />
|
|
42
|
+
</component>
|
|
43
|
+
<component name="ProjectId" id="2aegJrJPG76FhHan0nc8AGOXlKC" />
|
|
44
|
+
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
|
45
|
+
<component name="ProjectViewState">
|
|
46
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
47
|
+
<option name="showLibraryContents" value="true" />
|
|
48
|
+
</component>
|
|
49
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
50
|
+
"keyToString": {
|
|
51
|
+
"ASKED_ADD_EXTERNAL_FILES": "true",
|
|
52
|
+
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
|
53
|
+
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
|
54
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
55
|
+
"TODO_SCOPE": "All Places",
|
|
56
|
+
"last_opened_file_path": "/Users/christinestirrat/field-match",
|
|
57
|
+
"run.code.analysis.last.selected.profile": "pProject Default",
|
|
58
|
+
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
|
|
59
|
+
}
|
|
60
|
+
}]]></component>
|
|
61
|
+
<component name="RecentsManager">
|
|
62
|
+
<key name="CopyFile.RECENT_KEYS">
|
|
63
|
+
<recent name="$PROJECT_DIR$" />
|
|
64
|
+
</key>
|
|
65
|
+
<key name="MoveFile.RECENT_KEYS">
|
|
66
|
+
<recent name="$PROJECT_DIR$/field_match" />
|
|
67
|
+
<recent name="$PROJECT_DIR$/src/field_match" />
|
|
68
|
+
</key>
|
|
69
|
+
</component>
|
|
70
|
+
<component name="RunManager" selected="Python.test_field_match">
|
|
71
|
+
<configuration name="field_match" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
|
72
|
+
<module name="field-match" />
|
|
73
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
74
|
+
<option name="PARENT_ENVS" value="true" />
|
|
75
|
+
<envs>
|
|
76
|
+
<env name="PYTHONUNBUFFERED" value="1" />
|
|
77
|
+
</envs>
|
|
78
|
+
<option name="SDK_HOME" value="" />
|
|
79
|
+
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/src/field_match" />
|
|
80
|
+
<option name="IS_MODULE_SDK" value="true" />
|
|
81
|
+
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
82
|
+
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
83
|
+
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/field_match/field_match.py" />
|
|
84
|
+
<option name="PARAMETERS" value="" />
|
|
85
|
+
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
86
|
+
<option name="EMULATE_TERMINAL" value="false" />
|
|
87
|
+
<option name="MODULE_MODE" value="false" />
|
|
88
|
+
<option name="REDIRECT_INPUT" value="false" />
|
|
89
|
+
<option name="INPUT_FILE" value="" />
|
|
90
|
+
<method v="2" />
|
|
91
|
+
</configuration>
|
|
92
|
+
<configuration name="test_field_match" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
|
93
|
+
<module name="field-match" />
|
|
94
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
95
|
+
<option name="PARENT_ENVS" value="true" />
|
|
96
|
+
<envs>
|
|
97
|
+
<env name="PYTHONUNBUFFERED" value="1" />
|
|
98
|
+
</envs>
|
|
99
|
+
<option name="SDK_HOME" value="" />
|
|
100
|
+
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
|
|
101
|
+
<option name="IS_MODULE_SDK" value="true" />
|
|
102
|
+
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
103
|
+
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
104
|
+
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/tests/test_field_match.py" />
|
|
105
|
+
<option name="PARAMETERS" value="" />
|
|
106
|
+
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
107
|
+
<option name="EMULATE_TERMINAL" value="false" />
|
|
108
|
+
<option name="MODULE_MODE" value="false" />
|
|
109
|
+
<option name="REDIRECT_INPUT" value="false" />
|
|
110
|
+
<option name="INPUT_FILE" value="" />
|
|
111
|
+
<method v="2" />
|
|
112
|
+
</configuration>
|
|
113
|
+
<configuration name="usage" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
|
114
|
+
<module name="field-match" />
|
|
115
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
116
|
+
<option name="PARENT_ENVS" value="true" />
|
|
117
|
+
<envs>
|
|
118
|
+
<env name="PYTHONUNBUFFERED" value="1" />
|
|
119
|
+
</envs>
|
|
120
|
+
<option name="SDK_HOME" value="" />
|
|
121
|
+
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/examples" />
|
|
122
|
+
<option name="IS_MODULE_SDK" value="true" />
|
|
123
|
+
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
124
|
+
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
125
|
+
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/examples/usage.py" />
|
|
126
|
+
<option name="PARAMETERS" value="" />
|
|
127
|
+
<option name="SHOW_COMMAND_LINE" value="false" />
|
|
128
|
+
<option name="EMULATE_TERMINAL" value="false" />
|
|
129
|
+
<option name="MODULE_MODE" value="false" />
|
|
130
|
+
<option name="REDIRECT_INPUT" value="false" />
|
|
131
|
+
<option name="INPUT_FILE" value="" />
|
|
132
|
+
<method v="2" />
|
|
133
|
+
</configuration>
|
|
134
|
+
<recent_temporary>
|
|
135
|
+
<list>
|
|
136
|
+
<item itemvalue="Python.test_field_match" />
|
|
137
|
+
<item itemvalue="Python.usage" />
|
|
138
|
+
<item itemvalue="Python.field_match" />
|
|
139
|
+
</list>
|
|
140
|
+
</recent_temporary>
|
|
141
|
+
</component>
|
|
142
|
+
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
143
|
+
<component name="TaskManager">
|
|
144
|
+
<task active="true" id="Default" summary="Default task">
|
|
145
|
+
<changelist id="eb64838e-f39e-4f99-94ef-8fb088601c45" name="Changes" comment="" />
|
|
146
|
+
<created>1704684389781</created>
|
|
147
|
+
<option name="number" value="Default" />
|
|
148
|
+
<option name="presentableId" value="Default" />
|
|
149
|
+
<updated>1704684389781</updated>
|
|
150
|
+
</task>
|
|
151
|
+
<task id="LOCAL-00001" summary="Add files">
|
|
152
|
+
<created>1704768110097</created>
|
|
153
|
+
<option name="number" value="00001" />
|
|
154
|
+
<option name="presentableId" value="LOCAL-00001" />
|
|
155
|
+
<option name="project" value="LOCAL" />
|
|
156
|
+
<updated>1704768110097</updated>
|
|
157
|
+
</task>
|
|
158
|
+
<task id="LOCAL-00002" summary="Update CI">
|
|
159
|
+
<created>1704768383123</created>
|
|
160
|
+
<option name="number" value="00002" />
|
|
161
|
+
<option name="presentableId" value="LOCAL-00002" />
|
|
162
|
+
<option name="project" value="LOCAL" />
|
|
163
|
+
<updated>1704768383124</updated>
|
|
164
|
+
</task>
|
|
165
|
+
<option name="localTasksCounter" value="3" />
|
|
166
|
+
<servers />
|
|
167
|
+
</component>
|
|
168
|
+
<component name="UpdateCopyrightCheckinHandler">
|
|
169
|
+
<option name="UPDATE_COPYRIGHT" value="true" />
|
|
170
|
+
</component>
|
|
171
|
+
<component name="VcsManagerConfiguration">
|
|
172
|
+
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
|
|
173
|
+
<option name="CHECK_CODE_CLEANUP_BEFORE_PROJECT_COMMIT" value="true" />
|
|
174
|
+
<MESSAGE value="Add files" />
|
|
175
|
+
<MESSAGE value="Update CI" />
|
|
176
|
+
<option name="LAST_COMMIT_MESSAGE" value="Update CI" />
|
|
177
|
+
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
|
|
178
|
+
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
|
|
179
|
+
<option name="REARRANGE_BEFORE_PROJECT_COMMIT" value="true" />
|
|
180
|
+
</component>
|
|
181
|
+
</project>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Christine Stirrat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: field_match
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Identify corresponding fields between datasets
|
|
5
|
+
Project-URL: Homepage, https://github.com/cstirry/field-match
|
|
6
|
+
Author-email: Christine Stirrat <christine.stirrat@gmail.com>
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# field-match
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
field-match is a Python library designed to analyze and compare fields between two datasets based on similarity scores,
|
|
19
|
+
and match up the field names.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
To install the package, run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install field-match
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Import and use the package as follows:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import pandas as pd
|
|
35
|
+
from field_match import field_similarity_report, generate_column_rename
|
|
36
|
+
|
|
37
|
+
# Load your datasets
|
|
38
|
+
df1 = pd.read_csv('dataset1.csv')
|
|
39
|
+
df2 = pd.read_csv('dataset2.csv')
|
|
40
|
+
|
|
41
|
+
# Use the field_similarity_report function to match up fields by similarity score
|
|
42
|
+
results = field_similarity_report(df1, df2)
|
|
43
|
+
|
|
44
|
+
# Generate the column rename code snippet
|
|
45
|
+
rename_snippet = generate_column_rename(results)
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For more detailed examples, please refer to the `examples` folder in this repository.
|
|
50
|
+
|
|
51
|
+
## Example Applications
|
|
52
|
+
|
|
53
|
+
1. **Clarifying Ambiguous Dataset Fields:** When merging or integrating a new or updated dataset (such as data from a
|
|
54
|
+
another year or source) with an existing dataset or workflow, uncertainty in how fields correspond to each
|
|
55
|
+
may exist because of a lack of headers or differing field names. field_match can help identify which fields in the
|
|
56
|
+
new
|
|
57
|
+
dataset correspond to expected fields.
|
|
58
|
+
|
|
59
|
+
2. **Integrating External Dataset with Existing Model:** When feeding an external dataset into a pre-existing model,
|
|
60
|
+
it's crucial to ensure that the data aligns correctly with the model's expected input format. field_match can help
|
|
61
|
+
you
|
|
62
|
+
identify which fields in your new dataset correspond to the fields your model expects.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
field-match is released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# field-match
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
field-match is a Python library designed to analyze and compare fields between two datasets based on similarity scores,
|
|
6
|
+
and match up the field names.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
To install the package, run:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install field-match
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Import and use the package as follows:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import pandas as pd
|
|
22
|
+
from field_match import field_similarity_report, generate_column_rename
|
|
23
|
+
|
|
24
|
+
# Load your datasets
|
|
25
|
+
df1 = pd.read_csv('dataset1.csv')
|
|
26
|
+
df2 = pd.read_csv('dataset2.csv')
|
|
27
|
+
|
|
28
|
+
# Use the field_similarity_report function to match up fields by similarity score
|
|
29
|
+
results = field_similarity_report(df1, df2)
|
|
30
|
+
|
|
31
|
+
# Generate the column rename code snippet
|
|
32
|
+
rename_snippet = generate_column_rename(results)
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
For more detailed examples, please refer to the `examples` folder in this repository.
|
|
37
|
+
|
|
38
|
+
## Example Applications
|
|
39
|
+
|
|
40
|
+
1. **Clarifying Ambiguous Dataset Fields:** When merging or integrating a new or updated dataset (such as data from a
|
|
41
|
+
another year or source) with an existing dataset or workflow, uncertainty in how fields correspond to each
|
|
42
|
+
may exist because of a lack of headers or differing field names. field_match can help identify which fields in the
|
|
43
|
+
new
|
|
44
|
+
dataset correspond to expected fields.
|
|
45
|
+
|
|
46
|
+
2. **Integrating External Dataset with Existing Model:** When feeding an external dataset into a pre-existing model,
|
|
47
|
+
it's crucial to ensure that the data aligns correctly with the model's expected input format. field_match can help
|
|
48
|
+
you
|
|
49
|
+
identify which fields in your new dataset correspond to the fields your model expects.
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
field-match is released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from field_match import field_similarity_report, generate_column_rename
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
# Sample data for usage example
|
|
8
|
+
data1 = pd.DataFrame({
|
|
9
|
+
'product_id': [101, 102, 103],
|
|
10
|
+
'product_name': ['Apple', 'Banana', 'Cherry'],
|
|
11
|
+
'price': [1.20, 0.80, 2.50]
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
data2 = pd.DataFrame({
|
|
15
|
+
'id': [1, 2],
|
|
16
|
+
'item_code': [101, 202],
|
|
17
|
+
'description': ['Green Apple', 'Ripe Kiwi'],
|
|
18
|
+
'name': ['Apple', 'Kiwi'],
|
|
19
|
+
'cost': [1.10, 0.85]
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
# Using the field_similarity_report function
|
|
23
|
+
similarity_report = field_similarity_report(data1, data2)
|
|
24
|
+
print("Field Similarity Report:")
|
|
25
|
+
print(similarity_report)
|
|
26
|
+
# {'product_id': {'data_type': 'int64', 'similar_fields': [('item_code', 0.345), ('id', 0.309)]},
|
|
27
|
+
# 'product_name': {'data_type': 'object', 'similar_fields': [('name', 0.167), ('description', 0.105)]},
|
|
28
|
+
# 'price': {'data_type': 'float64', 'similar_fields': [('cost', 0.474)]}}
|
|
29
|
+
|
|
30
|
+
# Generating a code snippet for renaming columns in data2 based on the similarity report
|
|
31
|
+
rename_snippet = generate_column_rename(similarity_report)
|
|
32
|
+
print("\nSuggested Code Snippet for Renaming Columns:")
|
|
33
|
+
print(rename_snippet)
|
|
34
|
+
# "rename_dict = {
|
|
35
|
+
# 'item_code': 'product_id', # Similarity Score: 0.34
|
|
36
|
+
# 'cost': 'price', # Similarity Score: 0.47}
|
|
37
|
+
# data2.rename(columns=rename_dict, inplace=True)"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.build.targets.wheel]
|
|
6
|
+
packages = ["src/field_match"]
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "field_match"
|
|
10
|
+
version = "0.1.0"
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Christine Stirrat", email = "christine.stirrat@gmail.com" },
|
|
13
|
+
]
|
|
14
|
+
description = "Identify corresponding fields between datasets"
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
requires-python = ">=3.8"
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/cstirry/field-match"
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
4
|
+
from sklearn.metrics.pairwise import cosine_similarity
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def compute_text_similarity(series_1, series_2):
|
|
8
|
+
"""
|
|
9
|
+
Compute the cosine similarity between two series of text data.
|
|
10
|
+
|
|
11
|
+
This function uses TF-IDF (Term Frequency-Inverse Document Frequency) to convert
|
|
12
|
+
the text data into vectors and then computes the cosine similarity between these vectors.
|
|
13
|
+
It's a measure of similarity between two non-zero vectors of an inner product space
|
|
14
|
+
and is used to measure the cosine of the angle between them.
|
|
15
|
+
|
|
16
|
+
Parameters:
|
|
17
|
+
- series_1 (pandas.Series): A pandas Series containing text data.
|
|
18
|
+
- series_2 (pandas.Series): Another pandas Series containing text data to be
|
|
19
|
+
compared with series_1.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
- float: A single floating-point number representing the average cosine similarity
|
|
23
|
+
score between series_1 and series_2. The score ranges from 0 (no similarity)
|
|
24
|
+
to 1 (identical text content).
|
|
25
|
+
"""
|
|
26
|
+
vectorizer = TfidfVectorizer(stop_words='english')
|
|
27
|
+
combined_text = pd.concat([series_1.fillna(''), series_2.fillna('')], ignore_index=True)
|
|
28
|
+
tfidf_matrix = vectorizer.fit_transform(combined_text)
|
|
29
|
+
return cosine_similarity(tfidf_matrix[:len(series_1)], tfidf_matrix[len(series_1):]).mean()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def compute_numeric_similarity(series_1, series_2):
|
|
33
|
+
"""
|
|
34
|
+
Compute similarity for date fields based on the range of dates.
|
|
35
|
+
|
|
36
|
+
This function calculates the similarity between two date series by comparing the
|
|
37
|
+
range (difference between max and min dates) of each series. The similarity score
|
|
38
|
+
is inversely proportional to the difference in ranges between the two series.
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
- series_1 (pandas.Series): A pandas Series containing datetime data.
|
|
42
|
+
- series_2 (pandas.Series): Another pandas Series containing datetime data for comparison.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
- float: A similarity score ranging from 0 (no similarity) to 1 (identical ranges).
|
|
46
|
+
"""
|
|
47
|
+
stats_1 = series_1.describe()
|
|
48
|
+
stats_2 = series_2.describe()
|
|
49
|
+
similarities = {
|
|
50
|
+
'mean': 1 - abs(stats_1['mean'] - stats_2['mean']) / max(stats_1['mean'], stats_2['mean'], 1),
|
|
51
|
+
'median': 1 - abs(stats_1['50%'] - stats_2['50%']) / max(stats_1['50%'], stats_2['50%'], 1),
|
|
52
|
+
'std_dev': 1 - abs(stats_1['std'] - stats_2['std']) / max(stats_1['std'], stats_2['std'], 1),
|
|
53
|
+
'range': 1 - abs((stats_1['max'] - stats_1['min']) - (stats_2['max'] - stats_2['min'])) / max(
|
|
54
|
+
stats_1['max'] - stats_1['min'], stats_2['max'] - stats_2['min'], 1)
|
|
55
|
+
}
|
|
56
|
+
return np.mean(list(similarities.values()))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def compute_date_similarity(series_1, series_2):
|
|
60
|
+
"""
|
|
61
|
+
Compute similarity for date fields based on the range of dates.
|
|
62
|
+
|
|
63
|
+
This function calculates the similarity between two date series by comparing the
|
|
64
|
+
range (difference between max and min dates) of each series. The similarity score
|
|
65
|
+
is inversely proportional to the difference in ranges between the two series.
|
|
66
|
+
|
|
67
|
+
Parameters:
|
|
68
|
+
- series_1 (pandas.Series): A pandas Series containing datetime data.
|
|
69
|
+
- series_2 (pandas.Series): Another pandas Series containing datetime data for comparison.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
- float: A similarity score ranging from 0 (no similarity) to 1 (identical ranges).
|
|
73
|
+
"""
|
|
74
|
+
range1 = series_1.max() - series_1.min()
|
|
75
|
+
range2 = series_2.max() - series_2.min()
|
|
76
|
+
return 1 - abs((range1 - range2).days) / max(range1.days, range2.days)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def compute_boolean_similarity(series_1, series_2):
|
|
80
|
+
"""
|
|
81
|
+
Compute similarity for boolean fields based on value proportions.
|
|
82
|
+
|
|
83
|
+
This function assesses the similarity between two boolean series by comparing the
|
|
84
|
+
proportion of True values in each. It uses a close approximation to determine if the
|
|
85
|
+
proportions are similar.
|
|
86
|
+
|
|
87
|
+
Parameters:
|
|
88
|
+
- series_1 (pandas.Series): A pandas Series containing boolean data.
|
|
89
|
+
- series_2 (pandas.Series): Another pandas Series containing boolean data for comparison.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
- bool: True if the series are similar in their proportion of True values, False otherwise.
|
|
93
|
+
"""
|
|
94
|
+
proportion_1 = series_1.mean()
|
|
95
|
+
proportion_2 = series_2.mean()
|
|
96
|
+
|
|
97
|
+
# Use the smaller proportion as the base for comparison to avoid division by zero
|
|
98
|
+
base_proportion = min(proportion_1, proportion_2, 1 - proportion_1, 1 - proportion_2)
|
|
99
|
+
|
|
100
|
+
# Avoid division by zero; if both proportions are 0 or 1, they are identical
|
|
101
|
+
if base_proportion == 0:
|
|
102
|
+
return float(proportion_1 == proportion_2)
|
|
103
|
+
|
|
104
|
+
return 1 - abs(proportion_1 - proportion_2) / base_proportion
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def compute_categorical_similarity(series_1, series_2):
|
|
108
|
+
"""
|
|
109
|
+
Compute similarity for categorical data using Jaccard similarity.
|
|
110
|
+
|
|
111
|
+
This function evaluates the similarity between two categorical series by calculating
|
|
112
|
+
the Jaccard similarity, which is the ratio of the intersection to the union of the
|
|
113
|
+
unique values in both series.
|
|
114
|
+
|
|
115
|
+
Parameters:
|
|
116
|
+
- series_1 (pandas.Series): A pandas Series containing categorical data.
|
|
117
|
+
- series_2 (pandas.Series): Another pandas Series containing categorical data for comparison.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
- float: The Jaccard similarity score, ranging from 0 (no similarity) to 1 (identical categories).
|
|
121
|
+
"""
|
|
122
|
+
set1 = set(series_1.dropna().unique())
|
|
123
|
+
set2 = set(series_2.dropna().unique())
|
|
124
|
+
intersection = len(set1.intersection(set2))
|
|
125
|
+
union = len(set1.union(set2))
|
|
126
|
+
return intersection / union if union != 0 else 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def field_similarity_report(data1, data2):
|
|
130
|
+
"""
|
|
131
|
+
Compares fields between two pandas DataFrame objects and calculates similarity scores
|
|
132
|
+
for each pair of fields with the same data type.
|
|
133
|
+
|
|
134
|
+
This function iterates through each field in `data1` and compares it with every field in `data2`
|
|
135
|
+
of the same data type. For each field pair, it calculates a similarity score based on the
|
|
136
|
+
type of data they contain (e.g., text, numeric).
|
|
137
|
+
|
|
138
|
+
Parameters:
|
|
139
|
+
- data1 (pandas.DataFrame): The first DataFrame containing fields to be compared.
|
|
140
|
+
- data2 (pandas.DataFrame): The second DataFrame against which fields from data1 are compared.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
- dict: A dictionary where each key is a field name from data1, and the value is a list of tuples.
|
|
144
|
+
Each tuple contains a field name from data2 and its calculated similarity score with the key field.
|
|
145
|
+
The data type of each field from data1 is also included in the output.
|
|
146
|
+
"""
|
|
147
|
+
matched_fields_similarities = {}
|
|
148
|
+
for field1 in data1.columns:
|
|
149
|
+
field_similarities = []
|
|
150
|
+
for field2 in data2.columns:
|
|
151
|
+
if data1[field1].dtype == data2[field2].dtype:
|
|
152
|
+
similarity_score = 0
|
|
153
|
+
if pd.api.types.is_string_dtype(data1[field1]):
|
|
154
|
+
similarity_score = compute_text_similarity(data1[field1], data2[field2])
|
|
155
|
+
elif pd.api.types.is_numeric_dtype(data1[field1]):
|
|
156
|
+
similarity_score = compute_numeric_similarity(data1[field1], data2[field2])
|
|
157
|
+
elif pd.api.types.is_categorical_dtype(data1[field1]):
|
|
158
|
+
similarity_score = compute_categorical_similarity(data1[field1], data2[field2])
|
|
159
|
+
elif pd.api.types.is_datetime64_any_dtype(data1[field1]):
|
|
160
|
+
similarity_score = compute_date_similarity(data1[field1], data2[field2])
|
|
161
|
+
elif pd.api.types.is_bool_dtype(data1[field1]):
|
|
162
|
+
similarity_score = compute_boolean_similarity(data1[field1], data2[field2])
|
|
163
|
+
|
|
164
|
+
field_similarities.append((field2, round(similarity_score, 3)))
|
|
165
|
+
|
|
166
|
+
field_similarities.sort(key=lambda x: x[1], reverse=True)
|
|
167
|
+
matched_fields_similarities[field1] = {
|
|
168
|
+
"data_type": str(data1[field1].dtype),
|
|
169
|
+
"similar_fields": field_similarities
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return matched_fields_similarities
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def generate_column_rename(matched_fields_similarities):
|
|
176
|
+
"""
|
|
177
|
+
Generates a Python code snippet that suggests how to rename columns in the second DataFrame
|
|
178
|
+
based on the highest similarity scores from the field_similarity_report output.
|
|
179
|
+
|
|
180
|
+
Parameters:
|
|
181
|
+
- matched_fields_similarities (dict): The output dictionary from the field_similarity_report function.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
- str: A string containing the Python code snippet for renaming columns.
|
|
185
|
+
"""
|
|
186
|
+
rename_instr = "rename_dict = {\n"
|
|
187
|
+
for field1, match_info in matched_fields_similarities.items():
|
|
188
|
+
similar_fields = match_info.get('similar_fields', [])
|
|
189
|
+
if similar_fields: # Check if there are matching fields
|
|
190
|
+
most_similar_field, highest_score = similar_fields[0]
|
|
191
|
+
# Generate instruction only if the similarity score is above a certain threshold, e.g., 0.3
|
|
192
|
+
if highest_score > 0.3:
|
|
193
|
+
rename_instr += f" '{most_similar_field}': '{field1}', # Similarity Score: {highest_score:.2f}\n"
|
|
194
|
+
|
|
195
|
+
rename_instr += "}\ndata2.rename(columns=rename_dict, inplace=True)\n"
|
|
196
|
+
|
|
197
|
+
return rename_instr
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from src.field_match.field_match import (
|
|
8
|
+
compute_text_similarity,
|
|
9
|
+
compute_date_similarity,
|
|
10
|
+
compute_boolean_similarity,
|
|
11
|
+
compute_numeric_similarity,
|
|
12
|
+
compute_categorical_similarity,
|
|
13
|
+
field_similarity_report,
|
|
14
|
+
generate_column_rename
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestFieldMatch(unittest.TestCase):
|
|
19
|
+
"""Tests for `field_match` package."""
|
|
20
|
+
|
|
21
|
+
def test_compute_text_similarity(self):
|
|
22
|
+
series_1 = pd.Series(["apple", "banana", "cherry"])
|
|
23
|
+
series_2 = pd.Series(["apple pie", "banana split"])
|
|
24
|
+
result = compute_text_similarity(series_1, series_2)
|
|
25
|
+
self.assertTrue(0 <= result <= 1)
|
|
26
|
+
|
|
27
|
+
def test_compute_date_similarity(self):
|
|
28
|
+
series_1 = pd.to_datetime(pd.Series(["2021-01-01", "2021-01-02", "2021-01-03"]))
|
|
29
|
+
series_2 = pd.to_datetime(pd.Series(["2021-01-01", "2000-12-07"]))
|
|
30
|
+
result = compute_date_similarity(series_1, series_2)
|
|
31
|
+
self.assertTrue(0 <= result <= 1)
|
|
32
|
+
|
|
33
|
+
def test_compute_boolean_similarity(self):
|
|
34
|
+
series_1 = pd.Series([True, False, True])
|
|
35
|
+
series_2 = pd.Series([True, True])
|
|
36
|
+
result = compute_boolean_similarity(series_1, series_2)
|
|
37
|
+
self.assertTrue(0 <= result <= 1)
|
|
38
|
+
|
|
39
|
+
def test_compute_numeric_similarity(self):
|
|
40
|
+
series_1 = pd.Series([1, 2, 3])
|
|
41
|
+
series_2 = pd.Series([2, 3, 4])
|
|
42
|
+
result = compute_numeric_similarity(series_1, series_2)
|
|
43
|
+
self.assertTrue(0 <= result <= 1)
|
|
44
|
+
|
|
45
|
+
def test_compute_categorical_similarity(self):
|
|
46
|
+
series_1 = pd.Series(["cat", "dog", "bird"])
|
|
47
|
+
series_2 = pd.Series(["dog", "fly"])
|
|
48
|
+
result = compute_categorical_similarity(series_1, series_2)
|
|
49
|
+
self.assertTrue(0 <= result <= 1)
|
|
50
|
+
|
|
51
|
+
def test_field_similarity_report(self):
|
|
52
|
+
# Create sample data
|
|
53
|
+
data1 = pd.DataFrame({'A': ['apple', 'banana'], 'B': [1, 2]})
|
|
54
|
+
data2 = pd.DataFrame({'X': ['apple pie', 'banana bread'], 'Y': [2, 3]})
|
|
55
|
+
|
|
56
|
+
# Test field_similarity_report
|
|
57
|
+
report = field_similarity_report(data1, data2)
|
|
58
|
+
self.assertIsInstance(report, dict)
|
|
59
|
+
self.assertIn('A', report)
|
|
60
|
+
self.assertIn('B', report)
|
|
61
|
+
|
|
62
|
+
def test_generate_column_rename(self):
|
|
63
|
+
# Sample output from field_similarity_report
|
|
64
|
+
report = {
|
|
65
|
+
'A': {'data_type': 'int64', 'similar_fields': [('D', 0.9), ('E', 0.3)]},
|
|
66
|
+
'B': {'data_type': 'object', 'similar_fields': [('F', 0.6), ('G', 0.1)]},
|
|
67
|
+
'C': {'data_type': 'object', 'similar_fields': [('F', 0.2), ('G', 0.1)]},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Test generate_column_rename_snippet
|
|
71
|
+
snippet = generate_column_rename(report)
|
|
72
|
+
print(snippet)
|
|
73
|
+
self.assertIsInstance(snippet, str)
|
|
74
|
+
self.assertIn("'D': 'A'", snippet)
|
|
75
|
+
self.assertIn("'F': 'B'", snippet)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == '__main__':
|
|
79
|
+
unittest.main()
|