Skip to content

Commit cc36faf

Browse files
authored
Merge pull request #20 from pgolo/dev
Version 0.0.1
2 parents 1222765 + 83326c9 commit cc36faf

42 files changed

Lines changed: 3098 additions & 3 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
**/__pycache__/*
33
build/*
44
cythonized/*
5-
dist/*
5+
bin/*
66
*.spec
7+
pilsner.egg-info/*
78
!**/.gitkeep
8-
!**/.gitignore
9+
!**/.gitignore

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.0.1] - 2020-10-08
9+
10+
### Added
11+
12+
- Alpha version of the utility

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
include README.md
2+
include LICENSE

README.md

Lines changed: 261 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,262 @@
11
# pilsner
2-
Utility for dictionary-based named entity recognition
2+
3+
Python implemented library servicing named entity recognition
4+
5+
[![pypi][pypi-img]][pypi-url]
6+
7+
[pypi-img]: https://img.shields.io/pypi/v/pilsner?style=plastic
8+
[pypi-url]: https://pypi.org/project/pilsner/
9+
10+
## 1. Purpose
11+
12+
This library is Python implementation of toolkit for dictionary based named
13+
entity recognition. It is intended to store any thesaurus in a trie-like
14+
structure and identify any of stored synonyms in a string.
15+
16+
## 2. Installation and dependencies
17+
18+
```bash
19+
pip install pilsner
20+
```
21+
22+
`pilsner` is tested in Python 3.6, 3.7, and 3.8.
23+
24+
The only dependency is `sic` package. While it can be automatically installed
25+
at the time of `pilsner` installation, manual installation of `sic` beforehand
26+
might also be considered (see benchmark of cythonized vs pure Python
27+
implementation in `sic` docimentation,
28+
[https://pypi.org/project/sic/](https://pypi.org/project/sic/)).
29+
30+
## 3. Diagram
31+
32+
`pilsner` consists of two major components: `Model` and `Utility`. `Model`
33+
class provides storage for the dictionary and string normalization rules, as
34+
well as low-level methods for populating this storage. `Utility` class provides
35+
high-level methods for storing and retrieving data to/from `Model` instance.
36+
37+
![Diagram](misc/pilsner-diagram.svg)
38+
39+
## 4. Usage
40+
41+
```python
42+
import pilsner
43+
```
44+
45+
### 4.1. Initialize model
46+
47+
- To initialize empty model:
48+
49+
```python
50+
m = pilsner.Model()
51+
```
52+
53+
- To specify path to temporary database for empty model:
54+
55+
```python
56+
m = pilsner.Model(storage_location='path/to/database.file')
57+
```
58+
59+
- To create empty model that uses database created in memory rather than on
60+
disk:
61+
62+
```python
63+
m = pilsner.Model(storage_location=':memory:')
64+
```
65+
66+
> If database is created in memory, the model cannot be later saved on disk
67+
(can only be used instantly).
68+
69+
- To load model from disk:
70+
71+
```python
72+
m = pilsner.Model(filename='path/to/model')
73+
```
74+
75+
> More on how model is saved to and loaded from disk - see
76+
[4.6. Save model](#46-save-model) and [4.7. Load model](#47-load-model).
77+
78+
### 4.2. Add string normalization units
79+
80+
- Depending on the dictionary and nature of the text supposed to be parsed,
81+
string normalization might not be required at all, and nothing specific is to
82+
be done here in such case.
83+
- Without string normalization, synonyms from the dictionary will be stored as
84+
they are and looked up by recognizer case-sensitively.
85+
- To add a single normalization unit:
86+
87+
```python
88+
# Assuming m is pilsner.Model instance:
89+
m.add_normalizer(
90+
normalizer_name='normalizer_tag',
91+
filename='path/to/normalizer_config.xml'
92+
)
93+
```
94+
95+
> String normalization is technically done by `sic` component. See
96+
> documentation for `sic` at
97+
> [https://pypi.org/project/sic/](https://pypi.org/project/sic/) to learn how
98+
> to design normalizer config.
99+
100+
- Model can embed more than one normalization unit.
101+
- Default normalization unit for the model is the one added first or the last
102+
one added with parameter `default` set to `True`.
103+
- Having multiple normalization units in one model makes perfect sense when the
104+
stored dictionary contains synonyms of different nature that should be
105+
normalized in different ways (for example, abbreviations probably should not
106+
get normalized at all, while other synonyms might include tokens or punctuation
107+
marks that should not affect entity recognition). For that purpose, Model class
108+
includes `normalizer_map` dict that is supposed to map names of added
109+
normalization units to values in specific field in a dictionary designating the
110+
way a synonym should be normalized (tokenizer field, or tokenizer column):
111+
112+
```python
113+
# Assuming m is pilsner.Model instance:
114+
m.normalizer_map = {
115+
'synonym_type_1': 'normalizer_1',
116+
'synonym_type_2': 'normalizer_2'
117+
}
118+
```
119+
120+
> The snippet above instructs `pilsner` to normalize synonyms that have
121+
> `synonym_type_1` value in `tokenizer` column with `normalizer_1`
122+
> normalization unit, and normalize synonyms that have `synonym_type_2` value
123+
> in `tokenizer` column with `normalizer_2` normalization unit. For more about
124+
> fields in a dictionary, see [4.4. Define dictionary](#44-define-dictionary).
125+
126+
### 4.3. Initialize utility
127+
128+
- To load dictionary into `Model` instance, as well as to parse text, the
129+
`Utility` instance is required:
130+
131+
```python
132+
r = pilsner.Utility()
133+
```
134+
135+
### 4.4. Define dictionary
136+
137+
- Source dictionary for `pilsner` must be delimited text file.
138+
- Along with the source dictionary, specifications of the columns (fields) must
139+
be provided as list where each item corresponds to a column (from left to
140+
right). Each item in this list must be a dict object with string keys `name`,
141+
`include`, `delimiter`, `id_flag`, `normalizer_flag`, and `value_flag`, so
142+
that:
143+
- `field['name']` is a string for column title;
144+
- `field['include']` is a boolean that must be set to `True` for the column
145+
to be included in the model, otherwise `False`;
146+
- `field['delimiter']` is a string that is supposed to split single cell into
147+
list of values if the column holds concatenated lists rather than individual
148+
values;
149+
- `field['id_flag]` is a boolean that must be set to `True` if the column is
150+
supposed to be used for grouping synonyms (generally, entity ID is such
151+
column), otherwise `False`;
152+
- `field['normalizer_flag']` is a boolean that must be set to `True` if the
153+
column holds indication on what normalization unit must be applied to this
154+
particular synonym, otherwise `False`;
155+
- `field['value_flag']` is a boolean that must be set to `True` if the column
156+
holds synonyms that are supposed to be looked up when parsing a text,
157+
otherwise `False`.
158+
159+
> If dictionary has a column flagged with `normalizer_flag`, synonym in each
160+
> row will be normalized with string normalization unit which name is mapped on
161+
> value in this column using `pilsner.Model.normalizer_map` dict. If value is
162+
> not among `pilsner.Model.normalizer_map` keys, default normalization unit
163+
> will be used.
164+
165+
### 4.5. Compile model
166+
167+
- To store dictionary in `Model` instance, method `compile_model` of `Utility`
168+
instance must be called with the following required parameters:
169+
- `model`: pointer to initilized `Model` instance;
170+
- `filename`: string with path and filename of source dictionary;
171+
- `fields`: dict object with definitions of columns (see
172+
[4.4. Define dictionary](#44-define-dictionary));
173+
- `word_separator`: string defining what is to be considered word separator
174+
(generally, it should be whitespace);
175+
- `column_separator`: string defining what is to be considered column
176+
separator (e.g. `\t` for tab-delimited file);
177+
- `column_enclosure`: string defining what is to be stripped away from cell
178+
after row has been split into columns (typically, it should be `\n` for new
179+
line character to be trimmed from the rightmost column).
180+
181+
```python
182+
# Assuming m is pilsner.Model instance and r is pilsner.Utility instance:
183+
r.compile_model(
184+
model=m,
185+
filename='path/to/dictionary_in_a_text_file.txt',
186+
fields=fields,
187+
word_separator=' ',
188+
column_separator='\t',
189+
column_enclosure='\n'
190+
)
191+
```
192+
193+
- To review optional parameters, see comments in the code.
194+
195+
### 4.6. Save model
196+
197+
- If `Model` instance has compiled dictionary, and if database location for the
198+
`Model` instance is not explicitly set to `':memory:'`, the data such instance
199+
is holding can be saved to disk:
200+
201+
```python
202+
# Assuming m is pilsner.Model instance
203+
m.save('path/to/model_name')
204+
```
205+
206+
- The snippet above will write the following files:
207+
- `path/to/model_name.attributes`: database with attributes (fields from the
208+
dictionary that are not synonyms);
209+
- `path/to/model_name.keywords`: keywords used for disambiguation;
210+
- `path/to/model_name.normalizers`: string normalization units;
211+
- `path/to/model_name.0.dictionary`: trie with synonyms;
212+
- `path/to/model_name.<N>.dictionary`: additional tries with synonyms (`<N>`
213+
being integer number of a trie) in case more than one trie was created (see
214+
comments in the code - `pilsner.Utility.compile_model` method, `item_limit`
215+
parameter).
216+
217+
### 4.7. Load model
218+
219+
- To initialize new `Model` instance using previously saved data:
220+
221+
```python
222+
m = pilsner.Model(filename='path/to/model_name')
223+
```
224+
225+
- Alternatively, data can be loaded to previously initialized `Model` instance:
226+
227+
```python
228+
m = pilsner.Model()
229+
m.load('path/to/model_name')
230+
```
231+
232+
- In both cases, the program will look for the following files:
233+
- `path/to/model_name.attributes`: database with attributes (fields from the dictionary that are not synonyms);
234+
- `path/to/model_name.keywords`: keywords used for disambiguation;
235+
- `path/to/model_name.normalizers`: string normalization units;
236+
- `path/to/model_name.<N>.dictionary`: tries with synonyms (`<N>` being
237+
integer).
238+
239+
### 4.8. Parse string
240+
241+
- To parse a string without filtering out any synonyms and output all
242+
attributes of spotted entities:
243+
244+
```python
245+
# Assuming m is pilsner.Model instance, r is pilsner.Utility instance,
246+
# and text_to_parse is string to parse
247+
parsed = r.parse(
248+
model=m,
249+
source_string=text_to_parse
250+
)
251+
```
252+
253+
- The output will be dict object where keys are tuples for location of spotted
254+
entity in a string (begin, end) and values are dicts for attributes that are
255+
associated with identified entity (`{'attribute_name': {attribute_values}}`).
256+
- For details about optional parameters, see comments in the code -
257+
`pilsner.Utility.parse` function).
258+
259+
## 5. Example
260+
261+
Everything written above is put together in example code,
262+
see **/misc/example/** directory in the project's repository.
488 KB
Binary file not shown.
488 KB
Binary file not shown.
495 KB
Binary file not shown.

dist/pilsner-0.0.1.tar.gz

20.2 KB
Binary file not shown.

misc/example/custom_normalizer.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tokenizer name="plants">
3+
<setting name="cs" value="0" />
4+
<token to="lilies" from="lily" />
5+
</tokenizer>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tokenizer name="animals">
3+
<setting name="cs" value="0" />
4+
<split where="r" value="rerio" />
5+
<token to="" from="scary" />
6+
</tokenizer>

0 commit comments

Comments
 (0)