Repository: flairNLP/flair

Issue:
Simplifications in adding Label to Sentence
Adding a label to a `Sentence` currently requires instantiating a `Label`. To simplify, add some methods that take a string label and add a `Label` with default confidence 1. 

So instead of:
```python
sentence.add_label(Label('sports'))

sentence.add_labels([Label('sports'), Label('world cup')])

Sentence('France is the current world cup winner.', labels=[Label('sports'), Label('world cup')])
```
Something like:
```python
sentence.add_label('sports')

sentence.add_labels(['sports', 'world cup'])

Sentence('France is the current world cup winner.', labels=['sports', 'world cup'])
```



Discussion:

Resolving patch:
diff --git a/flair/data.py b/flair/data.py
--- a/flair/data.py
+++ b/flair/data.py
@@ -1,4 +1,4 @@
-from typing import List, Dict
+from typing import List, Dict, Union
 
 import torch
 
@@ -95,9 +95,10 @@ def load(cls, name: str):
 class Label:
     """
     This class represents a label of a sentence. Each label has a name and optional a confidence value. The confidence
-    value needs to be between 0.0 and 1.0. Default value for the confidence is 0.0.
+    value needs to be between 0.0 and 1.0. Default value for the confidence is 1.0.
     """
-    def __init__(self, name: str, confidence: float = 0.0):
+
+    def __init__(self, name: str, confidence: float = 1.0):
         self.name = name
         self.confidence = confidence
 
@@ -190,13 +191,14 @@ def embedding(self):
 
 
 class Sentence:
-    def __init__(self, text: str = None, use_tokenizer: bool = False, labels: List[Label] = None):
+    def __init__(self, text: str = None, use_tokenizer: bool = False, labels: Union[List[Label], List[str]] = None):
 
         super(Sentence, self).__init__()
 
         self.tokens: List[Token] = []
 
-        self.labels: List[Label] = labels
+        self.labels: List[Label] = []
+        if labels is not None: self.add_labels(labels)
 
         self._embeddings: Dict = {}
 
@@ -278,17 +280,16 @@ def __getitem__(self, idx: int) -> Token:
     def __iter__(self):
         return iter(self.tokens)
 
-    def add_label(self, label: Label):
-        if self.labels is None:
-            self.labels = [label]
-        else:
+    def add_label(self, label: Union[Label, str]):
+        if type(label) is Label:
             self.labels.append(label)
 
-    def add_labels(self, labels: List[Label]):
-        if self.labels is None:
-            self.labels = labels
-        else:
-            self.labels.extend(labels)
+        elif type(label) is str:
+            self.labels.append(Label(label))
+
+    def add_labels(self, labels: Union[List[Label], List[str]]):
+        for label in labels:
+            self.add_label(label)
 
     def get_label_names(self) -> List[str]:
         return [label.name for label in self.labels]
@@ -577,13 +578,13 @@ def iob_iobes(tags):
             new_tags.append(tag)
         elif tag.split('-')[0] == 'B':
             if i + 1 != len(tags) and \
-                            tags[i + 1].split('-')[0] == 'I':
+                    tags[i + 1].split('-')[0] == 'I':
                 new_tags.append(tag)
             else:
                 new_tags.append(tag.replace('B-', 'S-'))
         elif tag.split('-')[0] == 'I':
             if i + 1 < len(tags) and \
-                            tags[i + 1].split('-')[0] == 'I':
+                    tags[i + 1].split('-')[0] == 'I':
                 new_tags.append(tag)
             else:
                 new_tags.append(tag.replace('I-', 'E-'))
diff --git a/flair/data_fetcher.py b/flair/data_fetcher.py
--- a/flair/data_fetcher.py
+++ b/flair/data_fetcher.py
@@ -338,7 +338,7 @@ def read_text_classification_file(path_to_file):
                     if words[i].startswith(label_prefix):
                         l_len += len(words[i]) + 1
                         label = words[i].replace(label_prefix, "")
-                        labels.append(Label(label))
+                        labels.append(label)
                     else:
                         break
 


Test patch:
diff --git a/tests/test_data.py b/tests/test_data.py
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -255,6 +255,21 @@ def test_tagged_corpus_make_label_dictionary():
     assert ('class_2' in label_dict.get_items())
 
 
+def test_tagged_corpus_make_label_dictionary_string():
+    sentence_1 = Sentence('sentence 1', labels=['class_1'])
+    sentence_2 = Sentence('sentence 2', labels=['class_2'])
+    sentence_3 = Sentence('sentence 3', labels=['class_1'])
+
+    corpus: TaggedCorpus = TaggedCorpus([sentence_1, sentence_2, sentence_3], [], [])
+
+    label_dict = corpus.make_label_dictionary()
+
+    assert (2 == len(label_dict))
+    assert ('<unk>' not in label_dict.get_items())
+    assert ('class_1' in label_dict.get_items())
+    assert ('class_2' in label_dict.get_items())
+
+
 def test_tagged_corpus_statistics():
     train_sentence = Sentence('I love Berlin.', labels=[Label('class_1')], use_tokenizer=True)
     dev_sentence = Sentence('The sun is shining.', labels=[Label('class_2')], use_tokenizer=True)
@@ -275,6 +290,47 @@ def test_tagged_corpus_statistics():
     assert (4 == tokens_in_sentences[2])
 
 
+def test_tagged_corpus_statistics_string_label():
+    train_sentence = Sentence('I love Berlin.', labels=['class_1'], use_tokenizer=True)
+    dev_sentence = Sentence('The sun is shining.', labels=['class_2'], use_tokenizer=True)
+    test_sentence = Sentence('Berlin is sunny.', labels=['class_1'], use_tokenizer=True)
+
+    class_to_count_dict = TaggedCorpus._get_classes_to_count([train_sentence, dev_sentence, test_sentence])
+
+    assert ('class_1' in class_to_count_dict)
+    assert ('class_2' in class_to_count_dict)
+    assert (2 == class_to_count_dict['class_1'])
+    assert (1 == class_to_count_dict['class_2'])
+
+    tokens_in_sentences = TaggedCorpus._get_tokens_per_sentence([train_sentence, dev_sentence, test_sentence])
+
+    assert (3 == len(tokens_in_sentences))
+    assert (4 == tokens_in_sentences[0])
+    assert (5 == tokens_in_sentences[1])
+    assert (4 == tokens_in_sentences[2])
+
+
+def test_tagged_corpus_statistics_multi_label():
+    train_sentence = Sentence('I love Berlin.', labels=['class_1'], use_tokenizer=True)
+    dev_sentence = Sentence('The sun is shining.', labels=['class_2'], use_tokenizer=True)
+    test_sentence = Sentence('Berlin is sunny.', labels=['class_1', 'class_2'], use_tokenizer=True)
+
+    class_to_count_dict = TaggedCorpus._get_classes_to_count([train_sentence, dev_sentence, test_sentence])
+
+    assert ('class_1' in class_to_count_dict)
+    assert ('class_2' in class_to_count_dict)
+    assert (2 == class_to_count_dict['class_1'])
+    assert (2 == class_to_count_dict['class_2'])
+
+    tokens_in_sentences = TaggedCorpus._get_tokens_per_sentence([train_sentence, dev_sentence, test_sentence])
+
+    assert (3 == len(tokens_in_sentences))
+    assert (4 == tokens_in_sentences[0])
+    assert (5 == tokens_in_sentences[1])
+    assert (4 == tokens_in_sentences[2])
+
+
+
 def test_tagged_corpus_downsample():
     sentence = Sentence('I love Berlin.', labels=[Label('class_1')], use_tokenizer=True)