├── yapf.ini ├── .github ├── FUNDING.yml └── workflows │ ├── python_publish.yml │ └── python_test.yml ├── examples └── img │ ├── dark_img.png │ ├── pandas_img.png │ ├── sample_img.png │ └── japanese_img.png ├── cloudia ├── __init__.py ├── utils.py ├── pandas_accessor.py ├── main.py └── word_data.py ├── .gitignore ├── test ├── integration_test │ ├── test_cloudia_pandas_plot.py │ └── test_cloudia_plot.py └── unit_test │ ├── test_utils.py │ ├── test_main.py │ └── test_word_data.py ├── pyproject.toml ├── LICENSE ├── README.md └── poetry.lock /yapf.ini: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = pep8 3 | column_limit = 160 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: vaaaaanquish 4 | -------------------------------------------------------------------------------- /examples/img/dark_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaaaaanquish/cloudia/HEAD/examples/img/dark_img.png -------------------------------------------------------------------------------- /examples/img/pandas_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaaaaanquish/cloudia/HEAD/examples/img/pandas_img.png -------------------------------------------------------------------------------- /examples/img/sample_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaaaaanquish/cloudia/HEAD/examples/img/sample_img.png -------------------------------------------------------------------------------- /examples/img/japanese_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vaaaaanquish/cloudia/HEAD/examples/img/japanese_img.png -------------------------------------------------------------------------------- /cloudia/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import Cloudia 2 | from .pandas_accessor import CloudiaDataFrame, CloudiaSeries 3 | from .word_data import WordData 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Python 3rd Party 23 | Pipfile* 24 | 25 | # Installer logs 26 | pip-log.txt 27 | 28 | # Unit test / coverage reports 29 | .coverage 30 | .tox 31 | nosetests.xml 32 | .pytest_cache 33 | 34 | # Translations 35 | *.mo 36 | 37 | # Mr Developer 38 | .mr.developer.cfg 39 | .project 40 | .pydevproject 41 | .mypy_cache 42 | 43 | # Generated documentation 44 | docs/_build 45 | 46 | # pycharm metadata 47 | .idea 48 | 49 | # for Mac 50 | .DS_Store 51 | -------------------------------------------------------------------------------- /test/integration_test/test_cloudia_pandas_plot.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pathlib 3 | import traceback 4 | import pandas as pd 5 | 6 | if __name__ == '__main__': 7 | current_dir = pathlib.Path(__file__).resolve().parent 8 | sys.path.append(str(current_dir.parents[1])) 9 | import cloudia # noqa 10 | 11 | try: 12 | for multiprocess in [True, False]: 13 | pd.DataFrame({'test': ['hoge']}).wc.plot(multiprocess=multiprocess) 14 | pd.DataFrame({'test': ['hoge']})['test'].wc.plot(multiprocess=multiprocess) 15 | pd.Series(['hoge']).wc.plot(multiprocess=multiprocess) 16 | except Exception: 17 | traceback.print_exc() 18 | sys.exit(1) 19 | sys.exit(0) 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "cloudia" 3 | version = "0.0.1" 4 | description = "Tools to easily create a word cloud" 5 | authors = ["vaaaaanquish <6syun9@gmail.com>"] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/vaaaaanquish/cloudia" 9 | repository = "https://github.com/vaaaaanquish/cloudia" 10 | documentation = "" 11 | keywords = ["wordcloud", "matplotlib"] 12 | 13 | [tool.poetry-dynamic-versioning] 14 | enable = true 15 | style = "pep440" 16 | 17 | [tool.poetry.dependencies] 18 | python = "^3.6" 19 | nagisa = "*" 20 | wordcloud = "*" 21 | pandas = "*" 22 | matplotlib = "*" 23 | wurlitzer = "*" 24 | joblib = "*" 25 | japanize_matplotlib = "^1.1.1" 26 | 27 | [tool.poetry.scripts] 28 | my-script = 'cloudia:main' 29 | 30 | [build-system] 31 | requires = ["poetry"] 32 | build-backend = "poetry.masonry.api" 33 | -------------------------------------------------------------------------------- /test/unit_test/test_utils.py: -------------------------------------------------------------------------------- 1 | from cloudia.utils import default_parse_func, function_wrapper 2 | import unittest 3 | from collections import Counter 4 | 5 | 6 | class TestUtils(unittest.TestCase): 7 | def test_default_parse_func(self): 8 | output = default_parse_func('This is a simple test.', ['simple test'], ['英単語'], ['is'], 'default') 9 | self.assertListEqual(output, ['this', 'simple\u3000test']) 10 | 11 | def test_function_wrapper(self): 12 | def test(x): 13 | return [x + '_'] 14 | 15 | wf = function_wrapper(test) 16 | output = [wf(x, _index=i) for i, x in enumerate(['hoge', 'piyo'])] 17 | target = [(Counter({'hoge_': 1}), 0), (Counter({'piyo_': 1}), 1)] 18 | for o, t in zip(output, target): 19 | self.assertEqual(type(o), type(t)) 20 | self.assertEqual(o[1], t[1]) 21 | self.assertEqual(o[0].most_common(), t[0].most_common()) 22 | -------------------------------------------------------------------------------- /.github/workflows/python_publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v1 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install poetry poetry-dynamic-versioning twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | poetry publish --build --username ${TWINE_USERNAME} --password ${TWINE_PASSWORD} 31 | -------------------------------------------------------------------------------- /test/integration_test/test_cloudia_plot.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pathlib 3 | import traceback 4 | import pandas as pd 5 | 6 | if __name__ == '__main__': 7 | current_dir = pathlib.Path(__file__).resolve().parent 8 | sys.path.append(str(current_dir.parents[1])) 9 | from cloudia.main import Cloudia 10 | 11 | try: 12 | for multiprocess in [True, False]: 13 | Cloudia([('test', pd.Series(['hoge']))], multiprocess=multiprocess).plot() 14 | Cloudia([('test', 'hoge')], multiprocess=multiprocess).plot() 15 | Cloudia(['hoge'], multiprocess=multiprocess).plot() 16 | Cloudia([pd.Series(['hoge'])], multiprocess=multiprocess).plot() 17 | Cloudia('hoge', multiprocess=multiprocess).plot() 18 | Cloudia(('test', 'hoge'), multiprocess=multiprocess).plot() 19 | Cloudia(pd.DataFrame({'test': ['hoge']}), multiprocess=multiprocess).plot() 20 | Cloudia(pd.Series(['hoge']), multiprocess=multiprocess).plot() 21 | except Exception: 22 | traceback.print_exc() 23 | sys.exit(1) 24 | sys.exit(0) 25 | -------------------------------------------------------------------------------- /cloudia/utils.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from typing import List 3 | import re 4 | 5 | from wurlitzer import pipes 6 | 7 | with pipes() as (out, err): 8 | # https://github.com/clab/dynet/issues/1528 9 | import nagisa 10 | 11 | NUM_REGEX = re.compile('^[0-9]+$') 12 | 13 | 14 | def make_nagisa_tagger(single_words: List[str]): 15 | return nagisa.Tagger(single_word_list=single_words) 16 | 17 | 18 | def default_parse_func(text: str, single_words: List[str], extract_postags: List[str], stop_words: List[str], parser) -> List[str]: 19 | if parser == 'default': 20 | parser = make_nagisa_tagger(single_words) 21 | for x in ['"', ';', ',', '(', ')', '\u3000']: 22 | text = text.replace(x, ' ') 23 | text = text.lower() 24 | return [x for x in parser.extract(text, extract_postags=extract_postags).words if len(x) > 1 and not NUM_REGEX.match(x) and x not in stop_words] 25 | 26 | 27 | def function_wrapper(func): 28 | def _f(t, **kwargs): 29 | i = kwargs.pop('_index') 30 | d = Counter(func(t, **kwargs)) 31 | return d, i 32 | 33 | return _f 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020, vaaaaanquish 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. 22 | -------------------------------------------------------------------------------- /.github/workflows/python_test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v1 21 | with: 22 | python-version: 3.8 23 | - name: Install poetry dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install poetry yapf mypy 27 | poetry install 28 | - name: Lint with yapf 29 | run: | 30 | diff=$(yapf -dr --style=./yapf.ini ./cloudia/) 31 | if [ -n "$diffs" ]; then 32 | echo "failed: yapf" 33 | exit 1 34 | fi 35 | echo "pass yapf" 36 | - name: Lint with mypy 37 | run: | 38 | mypy --ignore-missing-imports ./cloudia/ 39 | if [ $? != 0 ]; then 40 | echo "failed: mypy" 41 | exit 1 42 | fi 43 | echo "pass mypy" 44 | - name: Unit Test 45 | run: | 46 | poetry run python -m unittest discover -s ./test/unit_test/ 47 | - name: Integration Test 48 | run: | 49 | poetry run python ./test/integration_test/test_cloudia_plot.py 50 | if [ $? != 0 ]; then 51 | echo "failed: cloudia_plot" 52 | exit 1 53 | fi 54 | echo "pass cloudia_plot" 55 | poetry run python ./test/integration_test/test_cloudia_pandas_plot.py 56 | if [ $? != 0 ]; then 57 | echo "failed: cloudia_pandas_plot" 58 | exit 1 59 | fi 60 | echo "pass cloudia_pandas_plot" 61 | -------------------------------------------------------------------------------- /test/unit_test/test_main.py: -------------------------------------------------------------------------------- 1 | from cloudia.main import CloudiaBase 2 | import unittest 3 | 4 | 5 | class TestCloudia(unittest.TestCase): 6 | # TODO: split test case 7 | def setUp(self): 8 | self.cls = CloudiaBase('test') 9 | 10 | def test_calc_fig_size(self): 11 | # row_num==item_num==1 12 | output = self.cls._calc_fig_size(1, 1, 1) 13 | self.assertTupleEqual(output, (10, 6)) 14 | 15 | # rate 16 | output = self.cls._calc_fig_size(1, 1, 2) 17 | self.assertTupleEqual(output, (20, 12)) 18 | 19 | # item_num<=row_num 20 | output = self.cls._calc_fig_size(1, 2, 1) 21 | self.assertTupleEqual(output, (5, 9)) 22 | 23 | output = self.cls._calc_fig_size(1, 2, 2) 24 | self.assertTupleEqual(output, (10, 18)) 25 | 26 | # item_num // row_num + 1 < row_num 27 | output = self.cls._calc_fig_size(2, 3, 1) 28 | self.assertTupleEqual(output, (10, 6)) 29 | 30 | output = self.cls._calc_fig_size(2, 3, 2) 31 | self.assertTupleEqual(output, (20, 12)) 32 | 33 | # else 34 | output = self.cls._calc_fig_size(3, 10, 1) 35 | self.assertTupleEqual(output, (15, 12)) 36 | 37 | output = self.cls._calc_fig_size(3, 10, 2) 38 | self.assertTupleEqual(output, (30, 24)) 39 | 40 | def test_calc_wc_size(self): 41 | output = self.cls._calc_wc_size(1) 42 | self.assertTupleEqual(output, (500, 300)) 43 | 44 | output = self.cls._calc_wc_size(2) 45 | self.assertTupleEqual(output, (1000, 600)) 46 | 47 | def test_calc_sub_plot_dimensions(self): 48 | output = self.cls._calc_sub_plot_dimensions(10, 3) 49 | self.assertTupleEqual(output, (4, 3)) 50 | 51 | output = self.cls._calc_sub_plot_dimensions(1, 3) 52 | self.assertTupleEqual(output, (1, 1)) 53 | 54 | output = self.cls._calc_sub_plot_dimensions(2, 2) 55 | self.assertTupleEqual(output, (2, 2)) 56 | 57 | def test_color(self): 58 | output = self.cls._color(True, True) 59 | self.assertEqual(output, 'white') 60 | 61 | output = self.cls._color(True, False) 62 | self.assertEqual(output, 'black') 63 | 64 | output = self.cls._color(False, True) 65 | self.assertEqual(output, 'black') 66 | 67 | output = self.cls._color(False, False) 68 | self.assertEqual(output, 'white') 69 | -------------------------------------------------------------------------------- /cloudia/pandas_accessor.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List 2 | 3 | import matplotlib.pyplot as plt 4 | from wordcloud import STOPWORDS 5 | import pandas as pd 6 | 7 | from cloudia.main import CloudiaBase, Cloudia 8 | from cloudia.utils import default_parse_func 9 | 10 | 11 | @pd.api.extensions.register_dataframe_accessor('wc') 12 | class CloudiaDataFrame(CloudiaBase): 13 | def __init__(self, df): 14 | self.df = df 15 | 16 | def plot(self, 17 | single_words: List[str] = [], 18 | stop_words: List[str] = STOPWORDS, 19 | extract_postags: List[str] = ['名詞', '英単語', 'ローマ字文'], 20 | parse_func: Any = default_parse_func, 21 | parser: Any = 'default', 22 | dark_theme: bool = False, 23 | title_size: int = 12, 24 | row_num: int = 3, 25 | figsize_rate: int = 2, 26 | multiprocess: bool = False, 27 | individual: bool = False, 28 | **args): 29 | Cloudia(self.df, single_words, stop_words, extract_postags, parse_func, parser, multiprocess, individual, 30 | **args).plot(dark_theme, title_size, row_num, figsize_rate) 31 | 32 | def save(self, fig_path: str, dark_theme: bool, **args: Any): 33 | self.plot(**args) 34 | plt.savefig(fig_path, facecolor=self._color(dark_theme), pad_inches=0.0, bbox_inches="tight") 35 | 36 | 37 | @pd.api.extensions.register_series_accessor('wc') 38 | class CloudiaSeries(CloudiaBase): 39 | def __init__(self, series): 40 | self.series = series 41 | 42 | def plot(self, 43 | single_words: List[str] = [], 44 | stop_words: List[str] = STOPWORDS, 45 | extract_postags: List[str] = ['名詞', '英単語', 'ローマ字文'], 46 | parse_func: Any = default_parse_func, 47 | parser: Any = 'default', 48 | dark_theme: bool = False, 49 | title_size: int = 12, 50 | row_num: int = 3, 51 | figsize_rate: int = 2, 52 | multiprocess: bool = False, 53 | individual: bool = False, 54 | **args): 55 | Cloudia(self.series, single_words, stop_words, extract_postags, parse_func, parser, multiprocess, individual, 56 | **args).plot(dark_theme, title_size, row_num, figsize_rate) 57 | 58 | def save(self, fig_path: str, dark_theme: bool, **args: Any): 59 | self.plot(**args) 60 | plt.savefig(fig_path, facecolor=self._color(dark_theme), pad_inches=0.0, bbox_inches="tight") 61 | -------------------------------------------------------------------------------- /cloudia/main.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Tuple 2 | 3 | import matplotlib.pyplot as plt 4 | import japanize_matplotlib 5 | from wordcloud import WordCloud, STOPWORDS 6 | 7 | from cloudia.word_data import WordData 8 | from cloudia.utils import default_parse_func 9 | 10 | 11 | class CloudiaBase: 12 | def __init__(self, 13 | data: Any, 14 | single_words: List[str] = [], 15 | stop_words: List[str] = STOPWORDS, 16 | extract_postags: List[str] = ['名詞', '英単語', 'ローマ字文'], 17 | parse_func: Any = default_parse_func, 18 | parser: Any = 'default', 19 | multiprocess: bool = False, 20 | individual: bool = False, 21 | **args): 22 | args.update(dict(single_words=single_words, stop_words=stop_words, extract_postags=extract_postags, parser=parser)) 23 | self.wd = WordData(data, parse_func, multiprocess, individual, **args) 24 | 25 | def make_wordcloud(self, dark_theme: bool, rate: int) -> List[Tuple[str, WordCloud]]: 26 | wordcloud_list = [] 27 | wcsize = self._calc_wc_size(rate) 28 | for name, words in self.wd: 29 | wordcloud = WordCloud(font_path=japanize_matplotlib.get_font_ttf_path(), 30 | background_color=self._color(dark_theme), 31 | width=wcsize[0], 32 | height=wcsize[1]) 33 | wordcloud.fit_words(words) 34 | wordcloud_list.append((name, wordcloud)) 35 | return wordcloud_list 36 | 37 | def make_fig(self, wordcloud_list: List[Tuple[str, WordCloud]], dark_theme: bool, title_size: int, row_num: int, rate: int): 38 | fig = plt.figure(facecolor=self._color(dark_theme), figsize=self._calc_fig_size(row_num, len(wordcloud_list), rate)) 39 | w, h = self._calc_sub_plot_dimensions(len(wordcloud_list), row_num) 40 | for i, (title, wc) in enumerate(wordcloud_list): 41 | ax = fig.add_subplot(w, h, i + 1) 42 | ax.imshow(wc) 43 | ax.set_title(title, color=self._color(dark_theme, True), fontsize=title_size) 44 | ax.axis('off') 45 | 46 | @staticmethod 47 | def _calc_fig_size(row_num: int, item_num: int, rate: int) -> Tuple[int, int]: 48 | if row_num == 1 and item_num == 1: 49 | return rate * 5 * 2, rate * 3 * 2 50 | if item_num <= row_num: 51 | return rate * 5 * item_num, rate * 3 * item_num 52 | elif item_num // row_num + 1 < row_num: 53 | return rate * 5 * row_num, rate * 3 * ((item_num // row_num + 1) % row_num) 54 | return rate * 5 * row_num, rate * 3 * (row_num + ((item_num // row_num + 1) - row_num)) 55 | 56 | @staticmethod 57 | def _calc_wc_size(rate: int) -> Tuple[int, int]: 58 | return rate * 5 * 100, rate * 3 * 100 59 | 60 | @staticmethod 61 | def _calc_sub_plot_dimensions(l: int, row_num: int) -> Tuple[int, int]: 62 | return (l // row_num) + 1, row_num if l > row_num else l 63 | 64 | @staticmethod 65 | def _color(dark_theme: bool, text: bool = False) -> str: 66 | if text: 67 | return 'white' if dark_theme else 'black' 68 | return 'black' if dark_theme else 'white' 69 | 70 | 71 | class Cloudia(CloudiaBase): 72 | def plot(self, dark_theme: bool = False, title_size: int = 12, row_num: int = 3, figsize_rate: int = 2): 73 | wc = self.make_wordcloud(dark_theme, figsize_rate) 74 | self.make_fig(wc, dark_theme, title_size, row_num, figsize_rate) 75 | 76 | def save(self, fig_path: str, dark_theme: bool = False, title_size: int = 12, row_num: int = 3, figsize_rate: int = 2): 77 | wc = self.make_wordcloud(dark_theme, figsize_rate) 78 | self.make_fig(wc, dark_theme, title_size, row_num, figsize_rate) 79 | plt.savefig(fig_path, facecolor=self._color(dark_theme), pad_inches=0.0, bbox_inches="tight") 80 | -------------------------------------------------------------------------------- /cloudia/word_data.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Tuple, Dict, Callable, Union 2 | from itertools import repeat, chain, zip_longest 3 | from collections import Counter 4 | 5 | from joblib import Parallel, delayed 6 | import pandas as pd 7 | 8 | from cloudia.utils import function_wrapper, make_nagisa_tagger 9 | 10 | 11 | class WordData: 12 | def __init__(self, data: Any, parse_func: Callable[..., List[str]], multiprocess: bool, individual: bool, **args): 13 | words, self.names = self._init_data(data) 14 | self.counter_list = self.parse(words, parse_func, multiprocess, individual, **args) 15 | self.words = [self.convert_weight(x) for x in self.counter_list] 16 | 17 | def parse(self, words, parse_func: Callable[..., List[str]], multiprocess: bool, individual: bool, **args) -> List[Counter]: 18 | if isinstance(words[0], list): 19 | word_list_length = len(words[0]) 20 | if individual: 21 | words = list(chain.from_iterable(words)) 22 | words = self._parse(words, parse_func, multiprocess, **args) 23 | words = list(zip_longest(*[iter(words)] * word_list_length)) 24 | words = [sum(w, Counter()) for w in words] 25 | else: 26 | words = [' '.join(x) for x in words] 27 | words = self._parse(words, parse_func, multiprocess, **args) 28 | else: 29 | words = self._parse(words, parse_func, multiprocess, **args) 30 | return words 31 | 32 | def convert_weight(self, c: Counter) -> Dict[str, float]: 33 | most_common = c.most_common() 34 | _max_count = most_common[0][1] 35 | weight = {k: v / _max_count for k, v in most_common} 36 | weight = {k: weight[k] for k in list(weight.keys())} 37 | return weight 38 | 39 | def _parse(self, words: List[str], parse_func: Callable[..., List[str]], multiprocess: bool, **args) -> Union[List[Counter], List[List[Counter]]]: 40 | if multiprocess: 41 | return self._parallel_parse(words, function_wrapper(parse_func), **args) 42 | return self._single_thread_parse(words, parse_func, **args) 43 | 44 | def _single_thread_parse(self, words: List[str], parse_func: Callable[..., List[str]], **args) -> List[Counter]: 45 | if args['parser'] == 'default': 46 | args.update({'parser': make_nagisa_tagger(args['single_words'])}) 47 | return [Counter(parse_func(x, **args)) for x in words] 48 | 49 | def _parallel_parse(self, words: List[str], parse_func: Callable, **args) -> List[List[Counter]]: 50 | parsed_words = Parallel(n_jobs=-1)([delayed(parse_func)(w, **dict(**a, **{'_index': i})) for i, (w, a) in enumerate(zip(words, repeat(args)))]) 51 | parsed_words.sort(key=lambda x: x[1]) 52 | parsed_words = [t[0] for t in parsed_words] 53 | return parsed_words 54 | 55 | def _init_data(self, data: Any) -> Tuple[List[str], List[str]]: 56 | # TODO: set assert 57 | words, names = [], [] 58 | if isinstance(data, list): 59 | if isinstance(data[0], tuple): 60 | if isinstance(data[0][1], pd.Series): 61 | words = [d.values.tolist() for n, d in data] 62 | names = [n for n, d in data] 63 | else: 64 | words = [w for n, w in data] 65 | names = [n for n, w in data] 66 | elif isinstance(data[0], str): 67 | words = data 68 | names = [f'word cloud {i+1}' for i in range(len(data))] 69 | elif isinstance(data[0], pd.Series): 70 | words = [d.values.tolist() for d in data] 71 | names = [d.name for d in data] 72 | elif isinstance(data, str): 73 | words = [data] 74 | names = ['word cloud'] 75 | elif isinstance(data, tuple): 76 | words = [data[1]] 77 | names = [data[0]] 78 | elif isinstance(data, pd.DataFrame): 79 | names = data.columns.tolist() 80 | words = [data[x].values.tolist() for x in names] 81 | elif isinstance(data, pd.Series): 82 | words = [data.values.tolist()] 83 | names = [data.name] 84 | return words, names 85 | 86 | def __iter__(self): 87 | for n, w in zip(self.names, self.words): 88 | yield n, w 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloudia 2 | Tools to easily create a word cloud. 3 | 4 | 5 | ### from string 6 | 7 | from str or List[str] 8 | ``` 9 | from cloudia import Cloudia 10 | 11 | text1 = "text data..." 12 | text2 = "text data..." 13 | 14 | # from str 15 | Cloudia(text1).plot() 16 | 17 | # from list 18 | Cloudia([text1, text2]).plot() 19 | ``` 20 | 21 | example from : [20 Newsgroups](http://qwone.com/~jason/20Newsgroups/) 22 | 23 | ![sample_img](https://github.com/vaaaaanquish/cloudia/blob/021a6d151fb6a3b579dc96b7086356fc0c225852/examples/img/sample_img.png?raw=true, "sample_img") 24 | 25 | 26 | We can also make it from Tuple. 27 | ``` 28 | from cloudia import Cloudia 29 | 30 | text1 = "text data..." 31 | text2 = "text data..." 32 | Cloudia([ ("cloudia 1", text1), ("cloudia 2", text2) ]).plot() 33 | ``` 34 | Tuple is ("IMAGE TITLE", "TEXT"). 35 | 36 | 37 | ### from pandas 38 | 39 | We can use pandas. 40 | 41 | ``` 42 | df = pd.DataFrame({'wc1': ['sample1','sample2'], 'wc2': ['hoge hoge piyo piyo fuga', 'hoge']}) 43 | 44 | # plot from df 45 | Cloudia(df).plot() 46 | 47 | # add df method 48 | df.wc.plot(dark_theme=True) 49 | ``` 50 | 51 | from pandas.DataFrame or pandas.Series. 52 | 53 | ![pandas_img](https://github.com/vaaaaanquish/cloudia/blob/021a6d151fb6a3b579dc96b7086356fc0c225852/examples/img/pandas_img.png?raw=true, "pandas_img") 54 | ![dark_img](https://github.com/vaaaaanquish/cloudia/blob/021a6d151fb6a3b579dc96b7086356fc0c225852/examples/img/dark_img.png?raw=true, "dark_img") 55 | 56 | We can use Tuple too. 57 | ``` 58 | Cloudia( ("IMAGE TITLE", pd.Series(['hoge'])) ).plot() 59 | ``` 60 | 61 | 62 | ### from japanese 63 | 64 | We can process Japanese too. 65 | ``` 66 | text = "これはCloudiaのテストです。WordCloudをつくるには本来、形態素解析の導入が必要になります。Cloudiaはmecabのような形態素解析器の導入は必要はなくnagisaを利用した動的な生成を行う事ができます。nagisaとjapanize-matplotlibは、形態素解析を必要としてきたWordCloud生成に対して、Cloudiaに対して大きく貢献しました。ここに感謝の意を述べたいと思います。" 67 | 68 | Cloudia(text).plot() 69 | ``` 70 | 71 | from japanese without morphological analysis module. 72 | 73 | ![japanese_img](https://github.com/vaaaaanquish/cloudia/blob/021a6d151fb6a3b579dc96b7086356fc0c225852/examples/img/japanese_img.png?raw=true, "jap_img") 74 | 75 | No need to introduce morphological analysis. 76 | 77 | 78 | # Install 79 | 80 | ``` 81 | pip install cloudia 82 | ``` 83 | 84 | 85 | # Args 86 | 87 | Cloudia args. 88 | ``` 89 | Cloudia( 90 | data, # text data 91 | single_words=[], # It's not split word list, example: ["neural network"] 92 | stop_words=STOPWORDS, # not count words, default is wordcloud.STOPWORDS 93 | extract_postags=['名詞', '英単語', 'ローマ字文'], # part of speech for japanese 94 | parse_func=None, # split text function, example: lambda x: x.split(',') 95 | multiprocess=True, # Flag for using multiprocessing 96 | individual=False # flag for ' '.join(word) with parse 97 | ) 98 | ``` 99 | 100 | 101 | plot method args. 102 | ``` 103 | Cloudia().plot( 104 | dark_theme=False, # color theme 105 | title_size=12, # title text size 106 | row_num=3, # for example, 12 wordcloud, row_num=3 -> 4*3image 107 | figsize_rate=2 # figure size rate 108 | ) 109 | ``` 110 | 111 | save method args. 112 | ``` 113 | Cloudia().save( 114 | file_path, # save figure image path 115 | dark_theme=False, 116 | title_size=12, 117 | row_num=3, 118 | figsize_rate=2 119 | ) 120 | ``` 121 | 122 | pandas.DataFrame, pandas.Series wc.plot method args. 123 | ``` 124 | DataFrame.wc.plot( 125 | single_words=[], # It's not split word list, example: ["neural network"] 126 | stop_words=STOPWORDS, # not count words, default is wordcloud.STOPWORDS 127 | extract_postags=['名詞', '英単語', 'ローマ字文'], # part of speech for japanese 128 | parse_func=None, # split text function, example: lambda x: x.split(',') 129 | multiprocess=True, # Flag for using multiprocessing 130 | individual=False, # flag for ' '.join(word) with parse 131 | dark_theme=False, # color theme 132 | title_size=12, # title text size 133 | row_num=3, # for example, 12 wordcloud, row_num=3 -> 4*3image 134 | figsize_rate=2 # figure size rate 135 | ) 136 | ``` 137 | If we use wc.save, setting file_path args. 138 | 139 | 140 | # Thanks 141 | 142 | - [japanize-matplotlib](https://github.com/uehara1414/japanize-matplotlib) 143 | - [nagisa](https://github.com/taishi-i/nagisa) 144 | -------------------------------------------------------------------------------- /test/unit_test/test_word_data.py: -------------------------------------------------------------------------------- 1 | from cloudia.word_data import WordData 2 | import unittest 3 | from unittest.mock import patch 4 | import pandas as pd 5 | from collections import Counter 6 | 7 | 8 | class TestWordData(unittest.TestCase): 9 | def setUp(self): 10 | self.cls = WordData('test', lambda x: [x], True, False) 11 | 12 | def assertSortTextEqual(self, data, target): 13 | """for random sample list.""" 14 | data = [sorted(t.split(' ')) if isinstance(t, str) else sorted(t) for t in data] 15 | target = [sorted(t.split(' ')) if isinstance(t, str) else sorted(t) for t in target] 16 | for x, y in zip(data, target): 17 | self.assertListEqual(x, y) 18 | 19 | def test_init_data_string(self): 20 | words, name = self.cls._init_data('test') 21 | self.assertListEqual(words, ['test']) 22 | self.assertListEqual(name, ['word cloud']) 23 | 24 | def test_init_data_tuple(self): 25 | words, name = self.cls._init_data(('name', 'test')) 26 | self.assertListEqual(words, ['test']) 27 | self.assertListEqual(name, ['name']) 28 | 29 | def test_init_data_list_string(self): 30 | words, name = self.cls._init_data(['test1 test2', 'test3']) 31 | self.assertSortTextEqual(words, ['test1 test2', 'test3']) 32 | self.assertListEqual(name, ['word cloud 1', 'word cloud 2']) 33 | 34 | def test_init_data_list_tuple_string(self): 35 | words, name = self.cls._init_data([('wc1', 'test1 test2'), ('wc2', 'test3')]) 36 | self.assertSortTextEqual(words, ['test1 test2', 'test3']) 37 | self.assertListEqual(name, ['wc1', 'wc2']) 38 | 39 | def test_init_data_list_tuple_series(self): 40 | test_1 = pd.Series(['test1 test2', 'test3'], name='wc1') 41 | test_2 = pd.Series(['test4', 'test5', 'test6'], name='wc2') 42 | words, name = self.cls._init_data([('name1', test_1), ('name2', test_2)]) 43 | self.assertSortTextEqual(words, [['test1 test2', 'test3'], 'test4 test5 test6']) 44 | self.assertListEqual(name, ['name1', 'name2']) 45 | 46 | def test_init_data_dataframe(self): 47 | test = pd.DataFrame({'wc1': ['test1', 'test2'], 'wc2': ['test3', 'test4']}) 48 | words, name = self.cls._init_data(test) 49 | self.assertSortTextEqual(words, ['test1 test2', 'test3 test4']) 50 | self.assertListEqual(name, ['wc1', 'wc2']) 51 | 52 | def test_init_data_series(self): 53 | test = pd.Series(['test1', 'test2'], name='wc') 54 | words, name = self.cls._init_data(test) 55 | self.assertSortTextEqual(words, ['test1 test2']) 56 | self.assertListEqual(name, ['wc']) 57 | 58 | def test_parse(self): 59 | def _parse(x, y, z, **args): 60 | return x 61 | 62 | with patch('cloudia.word_data.WordData._parse', side_effect=_parse): 63 | output = self.cls.parse(['hoge hoge', 'piyo'], None, None, False) 64 | self.assertListEqual(output, ['hoge hoge', 'piyo']) 65 | 66 | def test_parse_list_case(self): 67 | def _parse(x, y, z, **args): 68 | return [Counter(w.split(' ')) for w in x] 69 | 70 | with patch('cloudia.word_data.WordData._parse', side_effect=_parse): 71 | output = self.cls.parse([['hoge hoge', 'piyo'], ['fuga', 'fuga']], None, None, False) 72 | target = [Counter({'hoge': 2, 'piyo': 1}), Counter({'fuga': 2})] 73 | for o, t in zip(output, target): 74 | self.assertEqual(type(o), type(t)) 75 | self.assertEqual(o.most_common(), t.most_common()) 76 | 77 | def test_convert_weight(self): 78 | output = self.cls.convert_weight(Counter(['hoge', 'hoge', 'piyo'])) 79 | self.assertDictEqual(output, {'hoge': 1, 'piyo': 0.5}) 80 | 81 | def test_single_thread_parse(self): 82 | def f(x, parser, single_words): 83 | return x.split(' ') 84 | 85 | output = self.cls._single_thread_parse(['hoge hoge', 'piyo'], f, **{'parser': 'default', 'single_words': []}) 86 | target = [Counter(['hoge', 'hoge']), Counter(['piyo'])] 87 | for o, t in zip(output, target): 88 | self.assertEqual(type(o), type(t)) 89 | self.assertEqual(o.most_common(), t.most_common()) 90 | 91 | def test_parallel_parse(self): 92 | def f(x, _index): 93 | return Counter(x.split(' ')), _index 94 | 95 | output = self.cls._parallel_parse(['hoge hoge', 'piyo'], f, **{}) 96 | target = [ 97 | Counter(['hoge', 'hoge']), 98 | Counter(['piyo']), 99 | ] 100 | for o, t in zip(output, target): 101 | self.assertEqual(type(o), type(t)) 102 | self.assertEqual(o.most_common(), t.most_common()) 103 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "cycler" 3 | version = "0.10.0" 4 | description = "Composable style cycles" 5 | category = "main" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [package.dependencies] 10 | six = "*" 11 | 12 | [[package]] 13 | name = "cython" 14 | version = "0.29.21" 15 | description = "The Cython compiler for writing C extensions for the Python language." 16 | category = "main" 17 | optional = false 18 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 19 | 20 | [[package]] 21 | name = "dynet" 22 | version = "2.1.2" 23 | description = "The Dynamic Neural Network Toolkit" 24 | category = "main" 25 | optional = false 26 | python-versions = "*" 27 | 28 | [package.dependencies] 29 | cython = "*" 30 | numpy = "*" 31 | 32 | [[package]] 33 | name = "japanize-matplotlib" 34 | version = "1.1.3" 35 | description = "matplotlibのフォント設定を自動で日本語化する" 36 | category = "main" 37 | optional = false 38 | python-versions = "*" 39 | 40 | [package.dependencies] 41 | matplotlib = "*" 42 | 43 | [[package]] 44 | name = "joblib" 45 | version = "1.0.0" 46 | description = "Lightweight pipelining with Python functions" 47 | category = "main" 48 | optional = false 49 | python-versions = ">=3.6" 50 | 51 | [[package]] 52 | name = "kiwisolver" 53 | version = "1.3.1" 54 | description = "A fast implementation of the Cassowary constraint solver" 55 | category = "main" 56 | optional = false 57 | python-versions = ">=3.6" 58 | 59 | [[package]] 60 | name = "matplotlib" 61 | version = "3.3.3" 62 | description = "Python plotting package" 63 | category = "main" 64 | optional = false 65 | python-versions = ">=3.6" 66 | 67 | [package.dependencies] 68 | cycler = ">=0.10" 69 | kiwisolver = ">=1.0.1" 70 | numpy = ">=1.15" 71 | pillow = ">=6.2.0" 72 | pyparsing = ">=2.0.3,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6" 73 | python-dateutil = ">=2.1" 74 | 75 | [[package]] 76 | name = "nagisa" 77 | version = "0.2.7" 78 | description = "A Japanese tokenizer based on recurrent neural networks" 79 | category = "main" 80 | optional = false 81 | python-versions = "*" 82 | 83 | [package.dependencies] 84 | DyNet = "*" 85 | numpy = "*" 86 | six = "*" 87 | 88 | [[package]] 89 | name = "numpy" 90 | version = "1.19.4" 91 | description = "NumPy is the fundamental package for array computing with Python." 92 | category = "main" 93 | optional = false 94 | python-versions = ">=3.6" 95 | 96 | [[package]] 97 | name = "pandas" 98 | version = "0.25.3" 99 | description = "Powerful data structures for data analysis, time series, and statistics" 100 | category = "main" 101 | optional = false 102 | python-versions = ">=3.5.3" 103 | 104 | [package.dependencies] 105 | numpy = ">=1.13.3" 106 | python-dateutil = ">=2.6.1" 107 | pytz = ">=2017.2" 108 | 109 | [package.extras] 110 | test = ["pytest (>=4.0.2)", "pytest-xdist", "hypothesis (>=3.58)"] 111 | 112 | [[package]] 113 | name = "pillow" 114 | version = "8.0.1" 115 | description = "Python Imaging Library (Fork)" 116 | category = "main" 117 | optional = false 118 | python-versions = ">=3.6" 119 | 120 | [[package]] 121 | name = "pyparsing" 122 | version = "2.4.7" 123 | description = "Python parsing module" 124 | category = "main" 125 | optional = false 126 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 127 | 128 | [[package]] 129 | name = "python-dateutil" 130 | version = "2.8.1" 131 | description = "Extensions to the standard Python datetime module" 132 | category = "main" 133 | optional = false 134 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 135 | 136 | [package.dependencies] 137 | six = ">=1.5" 138 | 139 | [[package]] 140 | name = "pytz" 141 | version = "2020.5" 142 | description = "World timezone definitions, modern and historical" 143 | category = "main" 144 | optional = false 145 | python-versions = "*" 146 | 147 | [[package]] 148 | name = "six" 149 | version = "1.15.0" 150 | description = "Python 2 and 3 compatibility utilities" 151 | category = "main" 152 | optional = false 153 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 154 | 155 | [[package]] 156 | name = "wordcloud" 157 | version = "1.8.1" 158 | description = "A little word cloud generator" 159 | category = "main" 160 | optional = false 161 | python-versions = "*" 162 | 163 | [package.dependencies] 164 | matplotlib = "*" 165 | numpy = ">=1.6.1" 166 | pillow = "*" 167 | 168 | [[package]] 169 | name = "wurlitzer" 170 | version = "2.0.1" 171 | description = "Capture C-level output in context managers" 172 | category = "main" 173 | optional = false 174 | python-versions = ">=2.7" 175 | 176 | [metadata] 177 | lock-version = "1.1" 178 | python-versions = "^3.6" 179 | content-hash = "6e1e02815b139ab5162d0a2b90ec531ae6ed13fe54c692fde83cdf3ab5ffd970" 180 | 181 | [metadata.files] 182 | cycler = [ 183 | {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, 184 | {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"}, 185 | ] 186 | cython = [ 187 | {file = "Cython-0.29.21-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c541b2b49c6638f2b5beb9316726db84a8d1c132bf31b942dae1f9c7f6ad3b92"}, 188 | {file = "Cython-0.29.21-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b8d8497091c1dc8705d1575c71e908a93b1f127a174b2d472020f3d84263ac28"}, 189 | {file = "Cython-0.29.21-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:695a6bcaf9e12b1e471dfce96bbecf22a1487adc2ac6106b15960a2b51b97f5d"}, 190 | {file = "Cython-0.29.21-cp27-cp27m-win32.whl", hash = "sha256:171b9f70ceafcec5852089d0f9c1e75b0d554f46c882cd4e2e4acaba9bd7d148"}, 191 | {file = "Cython-0.29.21-cp27-cp27m-win_amd64.whl", hash = "sha256:539e59949aab4955c143a468810123bf22d3e8556421e1ce2531ed4893914ca0"}, 192 | {file = "Cython-0.29.21-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e93acd1f603a0c1786e0841f066ae7cef014cf4750e3cd06fd03cfdf46361419"}, 193 | {file = "Cython-0.29.21-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2922e3031ba9ebbe7cb9200b585cc33b71d66023d78450dcb883f824f4969371"}, 194 | {file = "Cython-0.29.21-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:497841897942f734b0abc2dead2d4009795ee992267a70a23485fd0e937edc0b"}, 195 | {file = "Cython-0.29.21-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:0ac10bf476476a9f7ef61ec6e44c280ef434473124ad31d3132b720f7b0e8d2a"}, 196 | {file = "Cython-0.29.21-cp34-cp34m-win32.whl", hash = "sha256:31c71a615f38401b0dc1f2a5a9a6c421ffd8908c4cd5bbedc4014c1b876488e8"}, 197 | {file = "Cython-0.29.21-cp34-cp34m-win_amd64.whl", hash = "sha256:c4b78356074fcaac04ecb4de289f11d506e438859877670992ece11f9c90f37b"}, 198 | {file = "Cython-0.29.21-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:b2f9172e4d6358f33ecce6a4339b5960f9f83eab67ea244baa812737793826b7"}, 199 | {file = "Cython-0.29.21-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:856c7fb31d247ce713d60116375e1f8153d0291ab5e92cca7d8833a524ba9991"}, 200 | {file = "Cython-0.29.21-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:715294cd2246b39a8edca464a8366eb635f17213e4a6b9e74e52d8b877a8cb63"}, 201 | {file = "Cython-0.29.21-cp35-cp35m-win32.whl", hash = "sha256:23f3a00b843a19de8bb4468b087db5b413a903213f67188729782488d67040e0"}, 202 | {file = "Cython-0.29.21-cp35-cp35m-win_amd64.whl", hash = "sha256:ccb77faeaad99e99c6c444d04862c6cf604204fe0a07d4c8f9cbf2c9012d7d5a"}, 203 | {file = "Cython-0.29.21-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e272ed97d20b026f4f25a012b25d7d7672a60e4f72b9ca385239d693cd91b2d5"}, 204 | {file = "Cython-0.29.21-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:8c6e25e9cc4961bb2abb1777c6fa9d0fa2d9b014beb3276cebe69996ff162b78"}, 205 | {file = "Cython-0.29.21-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:57ead89128dee9609119c93d3926c7a2add451453063147900408a50144598c6"}, 206 | {file = "Cython-0.29.21-cp36-cp36m-win32.whl", hash = "sha256:0e25c209c75df8785480dcef85db3d36c165dbc0f4c503168e8763eb735704f2"}, 207 | {file = "Cython-0.29.21-cp36-cp36m-win_amd64.whl", hash = "sha256:a0674f246ad5e1571ef29d4c5ec1d6ecabe9e6c424ad0d6fee46b914d5d24d69"}, 208 | {file = "Cython-0.29.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5da187bebe38030325e1c0b5b8a804d489410be2d384c0ef3ba39493c67eb51e"}, 209 | {file = "Cython-0.29.21-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9ce5e5209f8406ffc2b058b1293cce7a954911bb7991e623564d489197c9ba30"}, 210 | {file = "Cython-0.29.21-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5e545a48f919e40079b0efe7b0e081c74b96f9ef25b9c1ff4cdbd95764426b58"}, 211 | {file = "Cython-0.29.21-cp37-cp37m-win32.whl", hash = "sha256:c8435959321cf8aec867bbad54b83b7fb8343204b530d85d9ea7a1f5329d5ac2"}, 212 | {file = "Cython-0.29.21-cp37-cp37m-win_amd64.whl", hash = "sha256:540b3bee0711aac2e99bda4fa0a46dbcd8c74941666bfc1ef9236b1a64eeffd9"}, 213 | {file = "Cython-0.29.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93f5fed1c9445fb7afe20450cdaf94b0e0356d47cc75008105be89c6a2e417b1"}, 214 | {file = "Cython-0.29.21-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9207fdedc7e789a3dcaca628176b80c82fbed9ae0997210738cbb12536a56699"}, 215 | {file = "Cython-0.29.21-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:603b9f1b8e93e8b494d3e89320c410679e21018e48b6cbc77280f5db71f17dc0"}, 216 | {file = "Cython-0.29.21-cp38-cp38-win32.whl", hash = "sha256:473df5d5e400444a36ed81c6596f56a5b52a3481312d0a48d68b777790f730ae"}, 217 | {file = "Cython-0.29.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8a8a31b9e8860634adbca30fea1d0c7f08e208b3d7611f3e580e5f20992e5d7"}, 218 | {file = "Cython-0.29.21-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7ebaa8800c376bcdae596fb1372cb4232a5ef957619d35839520d2786f2debb9"}, 219 | {file = "Cython-0.29.21-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c111ac9abdf715762e4fb87395e59d61c0fbb6ce79eb2e24167700b6cfa8ba79"}, 220 | {file = "Cython-0.29.21-py2.py3-none-any.whl", hash = "sha256:5c4276fdcbccdf1e3c1756c7aeb8395e9a36874fa4d30860e7694f43d325ae13"}, 221 | {file = "Cython-0.29.21.tar.gz", hash = "sha256:e57acb89bd55943c8d8bf813763d20b9099cc7165c0f16b707631a7654be9cad"}, 222 | ] 223 | dynet = [ 224 | {file = "dyNET-2.1.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:0e1edb33dc81b3beddb13dc7801214efcde7d52433818e546835b8138ea0339d"}, 225 | {file = "dyNET-2.1.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c67cd81f3bf8b469da3efe083bdfab3f78363107a2af4257de7ce534275cf750"}, 226 | {file = "dyNET-2.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:dff73381e3f1353cba4101e2b3d10c7dfb492fe3fda0f8ac073ec4a3f30ab945"}, 227 | {file = "dyNET-2.1.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fea8b6bd75ed00f773e35ca29f6b9b1e8959af3bcc0e953712c1b3c02c5a0a45"}, 228 | {file = "dyNET-2.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:df9fc3df9cce6d7facf9550548ecbea842e4914c899d5435efde274e8d06e8a7"}, 229 | {file = "dyNET-2.1.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:489ce86861ded23f06ddf583b3e5d0d44bf97520f4a889696934ac2ab0ae5b4f"}, 230 | {file = "dyNET-2.1.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:3412c8941a840c149cc39ba9ab6e2475de6234917b0f73d8d206df698d621717"}, 231 | {file = "dyNET-2.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:996b623f24c1a1047d64552e79c5454b6ef71e2465f2f8051b2b0ddbaafcd517"}, 232 | {file = "dyNET-2.1.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:cd6349cbaf2361568343e9f747d66912844a6d176aece8a2cfa7db43bb0c7c2b"}, 233 | {file = "dyNET-2.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1916d0a62edc15f9a06268a1acf46c2eb636a2899f047af7b4accf49faaf6e55"}, 234 | {file = "dyNET-2.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e55fde2fd703a4e5f736ae7e691a042967eed7b7aae4921035d4273b64132817"}, 235 | {file = "dyNET-2.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a5952789b6c4838ce54151d72b590e6f142393b565ade0f27551f6f976797759"}, 236 | {file = "dyNET-2.1.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:356b359a59c762802510afd2dc016e8e093021b00d94fae86e9ba1959056734c"}, 237 | {file = "dyNET-2.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c96eca8afd1b1785c000460272c6339d67099964f5ec0b002c32da249da8d431"}, 238 | {file = "dyNET-2.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:46d1dd788ffe273311cc7fe1fdc24127ac32738138ced6a76ae6e090d43c3a0c"}, 239 | {file = "dyNET-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d2488763f8e7ecc426cd587c50c5f0caf98b8962e7353c08d4eb77ea86ab5356"}, 240 | {file = "dyNET-2.1.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:719e2c7327898c34e5003b88547a5b018c474be5ef211d6cd58ffd008c524339"}, 241 | {file = "dyNET-2.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8230e407705544ecfcece0f759502f2100787611a86041db2232608a9cca4ef2"}, 242 | {file = "dyNET-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fecb684fac6120d0be9d6e263cf01e6851ff2d30d26e07caeb1f29e716b19c46"}, 243 | {file = "dyNET-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b72f7cc07115734226f570f0b041f122c7b2b5f5984318eaf209112eb9aa139"}, 244 | {file = "dyNET-2.1.2.tar.gz", hash = "sha256:c1c1de7cdd3a60b6c6491868b0e096b38b88d4ce14ef8bd065cb76bcd9f5a8b9"}, 245 | ] 246 | japanize-matplotlib = [ 247 | {file = "japanize-matplotlib-1.1.3.tar.gz", hash = "sha256:e89e7d9e109820962650e59a130403b59b33915fde3871a265a5891d9bf5e079"}, 248 | ] 249 | joblib = [ 250 | {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, 251 | {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, 252 | ] 253 | kiwisolver = [ 254 | {file = "kiwisolver-1.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9"}, 255 | {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d3155d828dec1d43283bd24d3d3e0d9c7c350cdfcc0bd06c0ad1209c1bbc36d0"}, 256 | {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5a7a7dbff17e66fac9142ae2ecafb719393aaee6a3768c9de2fd425c63b53e21"}, 257 | {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f8d6f8db88049a699817fd9178782867bf22283e3813064302ac59f61d95be05"}, 258 | {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:5f6ccd3dd0b9739edcf407514016108e2280769c73a85b9e59aa390046dbf08b"}, 259 | {file = "kiwisolver-1.3.1-cp36-cp36m-win32.whl", hash = "sha256:225e2e18f271e0ed8157d7f4518ffbf99b9450fca398d561eb5c4a87d0986dd9"}, 260 | {file = "kiwisolver-1.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cf8b574c7b9aa060c62116d4181f3a1a4e821b2ec5cbfe3775809474113748d4"}, 261 | {file = "kiwisolver-1.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:232c9e11fd7ac3a470d65cd67e4359eee155ec57e822e5220322d7b2ac84fbf0"}, 262 | {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b38694dcdac990a743aa654037ff1188c7a9801ac3ccc548d3341014bc5ca278"}, 263 | {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ca3820eb7f7faf7f0aa88de0e54681bddcb46e485beb844fcecbcd1c8bd01689"}, 264 | {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fd0f1ae9d92b42854b2979024d7597685ce4ada367172ed7c09edf2cef9cb8"}, 265 | {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:1e1bc12fb773a7b2ffdeb8380609f4f8064777877b2225dec3da711b421fda31"}, 266 | {file = "kiwisolver-1.3.1-cp37-cp37m-win32.whl", hash = "sha256:72c99e39d005b793fb7d3d4e660aed6b6281b502e8c1eaf8ee8346023c8e03bc"}, 267 | {file = "kiwisolver-1.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8be8d84b7d4f2ba4ffff3665bcd0211318aa632395a1a41553250484a871d454"}, 268 | {file = "kiwisolver-1.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31dfd2ac56edc0ff9ac295193eeaea1c0c923c0355bf948fbd99ed6018010b72"}, 269 | {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:563c649cfdef27d081c84e72a03b48ea9408c16657500c312575ae9d9f7bc1c3"}, 270 | {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78751b33595f7f9511952e7e60ce858c6d64db2e062afb325985ddbd34b5c131"}, 271 | {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a357fd4f15ee49b4a98b44ec23a34a95f1e00292a139d6015c11f55774ef10de"}, 272 | {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:5989db3b3b34b76c09253deeaf7fbc2707616f130e166996606c284395da3f18"}, 273 | {file = "kiwisolver-1.3.1-cp38-cp38-win32.whl", hash = "sha256:c08e95114951dc2090c4a630c2385bef681cacf12636fb0241accdc6b303fd81"}, 274 | {file = "kiwisolver-1.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:44a62e24d9b01ba94ae7a4a6c3fb215dc4af1dde817e7498d901e229aaf50e4e"}, 275 | {file = "kiwisolver-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50af681a36b2a1dee1d3c169ade9fdc59207d3c31e522519181e12f1b3ba7000"}, 276 | {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a53d27d0c2a0ebd07e395e56a1fbdf75ffedc4a05943daf472af163413ce9598"}, 277 | {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:834ee27348c4aefc20b479335fd422a2c69db55f7d9ab61721ac8cd83eb78882"}, 278 | {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5c3e6455341008a054cccee8c5d24481bcfe1acdbc9add30aa95798e95c65621"}, 279 | {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:acef3d59d47dd85ecf909c359d0fd2c81ed33bdff70216d3956b463e12c38a54"}, 280 | {file = "kiwisolver-1.3.1-cp39-cp39-win32.whl", hash = "sha256:c5518d51a0735b1e6cee1fdce66359f8d2b59c3ca85dc2b0813a8aa86818a030"}, 281 | {file = "kiwisolver-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:b9edd0110a77fc321ab090aaa1cfcaba1d8499850a12848b81be2222eab648f6"}, 282 | {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0cd53f403202159b44528498de18f9285b04482bab2a6fc3f5dd8dbb9352e30d"}, 283 | {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:33449715e0101e4d34f64990352bce4095c8bf13bed1b390773fc0a7295967b3"}, 284 | {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:401a2e9afa8588589775fe34fc22d918ae839aaaf0c0e96441c0fdbce6d8ebe6"}, 285 | {file = "kiwisolver-1.3.1.tar.gz", hash = "sha256:950a199911a8d94683a6b10321f9345d5a3a8433ec58b217ace979e18f16e248"}, 286 | ] 287 | matplotlib = [ 288 | {file = "matplotlib-3.3.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b2a5e1f637a92bb6f3526cc54cc8af0401112e81ce5cba6368a1b7908f9e18bc"}, 289 | {file = "matplotlib-3.3.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c586ac1d64432f92857c3cf4478cfb0ece1ae18b740593f8a39f2f0b27c7fda5"}, 290 | {file = "matplotlib-3.3.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9b03722c89a43a61d4d148acfc89ec5bb54cd0fd1539df25b10eb9c5fa6c393a"}, 291 | {file = "matplotlib-3.3.3-cp36-cp36m-win32.whl", hash = "sha256:2c2c5041608cb75c39cbd0ed05256f8a563e144234a524c59d091abbfa7a868f"}, 292 | {file = "matplotlib-3.3.3-cp36-cp36m-win_amd64.whl", hash = "sha256:c092fc4673260b1446b8578015321081d5db73b94533fe4bf9b69f44e948d174"}, 293 | {file = "matplotlib-3.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:27c9393fada62bd0ad7c730562a0fecbd3d5aaa8d9ed80ba7d3ebb8abc4f0453"}, 294 | {file = "matplotlib-3.3.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b8ba2a1dbb4660cb469fe8e1febb5119506059e675180c51396e1723ff9b79d9"}, 295 | {file = "matplotlib-3.3.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0caa687fce6174fef9b27d45f8cc57cbc572e04e98c81db8e628b12b563d59a2"}, 296 | {file = "matplotlib-3.3.3-cp37-cp37m-win32.whl", hash = "sha256:b7b09c61a91b742cb5460b72efd1fe26ef83c1c704f666e0af0df156b046aada"}, 297 | {file = "matplotlib-3.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6ffd2d80d76df2e5f9f0c0140b5af97e3b87dd29852dcdb103ec177d853ec06b"}, 298 | {file = "matplotlib-3.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5111d6d47a0f5b8f3e10af7a79d5e7eb7e73a22825391834734274c4f312a8a0"}, 299 | {file = "matplotlib-3.3.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a4fe54eab2c7129add75154823e6543b10261f9b65b2abe692d68743a4999f8c"}, 300 | {file = "matplotlib-3.3.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:83e6c895d93fdf93eeff1a21ee96778ba65ef258e5d284160f7c628fee40c38f"}, 301 | {file = "matplotlib-3.3.3-cp38-cp38-win32.whl", hash = "sha256:b26c472847911f5a7eb49e1c888c31c77c4ddf8023c1545e0e8e0367ba74fb15"}, 302 | {file = "matplotlib-3.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:09225edca87a79815822eb7d3be63a83ebd4d9d98d5aa3a15a94f4eee2435954"}, 303 | {file = "matplotlib-3.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eb6b6700ea454bb88333d98601e74928e06f9669c1ea231b4c4c666c1d7701b4"}, 304 | {file = "matplotlib-3.3.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2d31aff0c8184b05006ad756b9a4dc2a0805e94d28f3abc3187e881b6673b302"}, 305 | {file = "matplotlib-3.3.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d082f77b4ed876ae94a9373f0db96bf8768a7cca6c58fc3038f94e30ffde1880"}, 306 | {file = "matplotlib-3.3.3-cp39-cp39-win32.whl", hash = "sha256:e71cdd402047e657c1662073e9361106c6981e9621ab8c249388dfc3ec1de07b"}, 307 | {file = "matplotlib-3.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:756ee498b9ba35460e4cbbd73f09018e906daa8537fff61da5b5bf8d5e9de5c7"}, 308 | {file = "matplotlib-3.3.3-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ad44f2c74c50567c694ee91c6fa16d67e7c8af6f22c656b80469ad927688457"}, 309 | {file = "matplotlib-3.3.3-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:3a4c3e9be63adf8e9b305aa58fb3ec40ecc61fd0f8fd3328ce55bc30e7a2aeb0"}, 310 | {file = "matplotlib-3.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:746897fbd72bd462b888c74ed35d812ca76006b04f717cd44698cdfc99aca70d"}, 311 | {file = "matplotlib-3.3.3-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:5ed3d3342698c2b1f3651f8ea6c099b0f196d16ee00e33dc3a6fee8cb01d530a"}, 312 | {file = "matplotlib-3.3.3.tar.gz", hash = "sha256:b1b60c6476c4cfe9e5cf8ab0d3127476fd3d5f05de0f343a452badaad0e4bdec"}, 313 | ] 314 | nagisa = [ 315 | {file = "nagisa-0.2.7-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:c12c3ac51f9aaaa7a5ff08a04d2fd57277f7bb97eacff744bfb8e86c47fca6ae"}, 316 | {file = "nagisa-0.2.7-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b009fbde4300dd37e688bc21c6de502645955728532907d1665dde4c8e71f6d2"}, 317 | {file = "nagisa-0.2.7-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:271f9c747f4ffe00580babd06a4edc164dab1198857433750bca25f172f8438d"}, 318 | {file = "nagisa-0.2.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:afa6673b846ebe0a5d9015bf78d1aeb82554b8cd023968b1ae8ecee2f46eb298"}, 319 | {file = "nagisa-0.2.7-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:b7537e3c4a14dc2decb5a3daa389a3c8d8fc079200fb4b56c079a66782fe5c1b"}, 320 | {file = "nagisa-0.2.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:23aa8e5eadf2502410a23863e6347b5763c86cae01ed7600dd4a632306670bca"}, 321 | {file = "nagisa-0.2.7-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:eb889e12d1f3a68fa7fb1440453aa9130c13244633ebd4f16b97d373dadbc122"}, 322 | {file = "nagisa-0.2.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:07fa155ec5fcb80208d31bb6fd7039d6c2988c3b82c0cdba4baf60624e936c62"}, 323 | {file = "nagisa-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:eb21e331064f9f8f3d9021761efc30ff94bb36b4ee7f0b9ce7a055c46abe93c0"}, 324 | {file = "nagisa-0.2.7-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2c70681c0dc6dc717f93dfb065a14916592a9c702752b2a2e31234dbb3f012f5"}, 325 | {file = "nagisa-0.2.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f4883c362440ec5594696e4c1f17831182d20cb845f2b080e838944d711c4c13"}, 326 | {file = "nagisa-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:8b948f73d4884057eb2c20c70a9462823e00779fc35de14afd848467b91299c3"}, 327 | {file = "nagisa-0.2.7-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b03e4cd471c3a76aebd45c2bba38233f34b23c100d289837d08d664ccc38ad58"}, 328 | {file = "nagisa-0.2.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d59b25461e9643e463a6f6eacb8182e591e7f8904215c7b4e94ff77b722267c4"}, 329 | {file = "nagisa-0.2.7-cp39-cp39-manylinux1_i686.whl", hash = "sha256:1cdc1e8f3149f5414d4572b122d794c0cd22f40dce67c8f4e53381edc34cd0ff"}, 330 | {file = "nagisa-0.2.7-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d409014216b1ce2984a988c3f7557aa0d82d7c6102a3fb6d3dba3f1909333d12"}, 331 | {file = "nagisa-0.2.7.tar.gz", hash = "sha256:1654650efbc3424fee90ba0259701949bc00843fd2b83bb27a27dddd96f0fbdc"}, 332 | ] 333 | numpy = [ 334 | {file = "numpy-1.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949"}, 335 | {file = "numpy-1.19.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"}, 336 | {file = "numpy-1.19.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad"}, 337 | {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83"}, 338 | {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764"}, 339 | {file = "numpy-1.19.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6"}, 340 | {file = "numpy-1.19.4-cp36-cp36m-win32.whl", hash = "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1"}, 341 | {file = "numpy-1.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb"}, 342 | {file = "numpy-1.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2"}, 343 | {file = "numpy-1.19.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2"}, 344 | {file = "numpy-1.19.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9"}, 345 | {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757"}, 346 | {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15"}, 347 | {file = "numpy-1.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387"}, 348 | {file = "numpy-1.19.4-cp37-cp37m-win32.whl", hash = "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36"}, 349 | {file = "numpy-1.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c"}, 350 | {file = "numpy-1.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909"}, 351 | {file = "numpy-1.19.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c"}, 352 | {file = "numpy-1.19.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893"}, 353 | {file = "numpy-1.19.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab"}, 354 | {file = "numpy-1.19.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9"}, 355 | {file = "numpy-1.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db"}, 356 | {file = "numpy-1.19.4-cp38-cp38-win32.whl", hash = "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac"}, 357 | {file = "numpy-1.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce"}, 358 | {file = "numpy-1.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63"}, 359 | {file = "numpy-1.19.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37"}, 360 | {file = "numpy-1.19.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414"}, 361 | {file = "numpy-1.19.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc"}, 362 | {file = "numpy-1.19.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3"}, 363 | {file = "numpy-1.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753"}, 364 | {file = "numpy-1.19.4-cp39-cp39-win32.whl", hash = "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f"}, 365 | {file = "numpy-1.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b"}, 366 | {file = "numpy-1.19.4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08"}, 367 | {file = "numpy-1.19.4.zip", hash = "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512"}, 368 | ] 369 | pandas = [ 370 | {file = "pandas-0.25.3-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:df8864824b1fe488cf778c3650ee59c3a0d8f42e53707de167ba6b4f7d35f133"}, 371 | {file = "pandas-0.25.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7458c48e3d15b8aaa7d575be60e1e4dd70348efcd9376656b72fecd55c59a4c3"}, 372 | {file = "pandas-0.25.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:61741f5aeb252f39c3031d11405305b6d10ce663c53bc3112705d7ad66c013d0"}, 373 | {file = "pandas-0.25.3-cp35-cp35m-win32.whl", hash = "sha256:adc3d3a3f9e59a38d923e90e20c4922fc62d1e5a03d083440468c6d8f3f1ae0a"}, 374 | {file = "pandas-0.25.3-cp35-cp35m-win_amd64.whl", hash = "sha256:975c461accd14e89d71772e89108a050fa824c0b87a67d34cedf245f6681fc17"}, 375 | {file = "pandas-0.25.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ee50c2142cdcf41995655d499a157d0a812fce55c97d9aad13bc1eef837ed36c"}, 376 | {file = "pandas-0.25.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4545467a637e0e1393f7d05d61dace89689ad6d6f66f267f86fff737b702cce9"}, 377 | {file = "pandas-0.25.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bbe3eb765a0b1e578833d243e2814b60c825b7fdbf4cdfe8e8aae8a08ed56ecf"}, 378 | {file = "pandas-0.25.3-cp36-cp36m-win32.whl", hash = "sha256:8153705d6545fd9eb6dd2bc79301bff08825d2e2f716d5dced48daafc2d0b81f"}, 379 | {file = "pandas-0.25.3-cp36-cp36m-win_amd64.whl", hash = "sha256:26382aab9c119735908d94d2c5c08020a4a0a82969b7e5eefb92f902b3b30ad7"}, 380 | {file = "pandas-0.25.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:00dff3a8e337f5ed7ad295d98a31821d3d0fe7792da82d78d7fd79b89c03ea9d"}, 381 | {file = "pandas-0.25.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e45055c30a608076e31a9fcd780a956ed3b1fa20db61561b8d88b79259f526f7"}, 382 | {file = "pandas-0.25.3-cp37-cp37m-win32.whl", hash = "sha256:255920e63850dc512ce356233081098554d641ba99c3767dde9e9f35630f994b"}, 383 | {file = "pandas-0.25.3-cp37-cp37m-win_amd64.whl", hash = "sha256:22361b1597c8c2ffd697aa9bf85423afa9e1fcfa6b1ea821054a244d5f24d75e"}, 384 | {file = "pandas-0.25.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9962957a27bfb70ab64103d0a7b42fa59c642fb4ed4cb75d0227b7bb9228535d"}, 385 | {file = "pandas-0.25.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78bf638993219311377ce9836b3dc05f627a666d0dbc8cec37c0ff3c9ada673b"}, 386 | {file = "pandas-0.25.3-cp38-cp38-win32.whl", hash = "sha256:6a3ac2c87e4e32a969921d1428525f09462770c349147aa8e9ab95f88c71ec71"}, 387 | {file = "pandas-0.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:33970f4cacdd9a0ddb8f21e151bfb9f178afb7c36eb7c25b9094c02876f385c2"}, 388 | {file = "pandas-0.25.3.tar.gz", hash = "sha256:52da74df8a9c9a103af0a72c9d5fdc8e0183a90884278db7f386b5692a2220a4"}, 389 | ] 390 | pillow = [ 391 | {file = "Pillow-8.0.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3"}, 392 | {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302"}, 393 | {file = "Pillow-8.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c"}, 394 | {file = "Pillow-8.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11"}, 395 | {file = "Pillow-8.0.1-cp36-cp36m-win32.whl", hash = "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e"}, 396 | {file = "Pillow-8.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3"}, 397 | {file = "Pillow-8.0.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09"}, 398 | {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae"}, 399 | {file = "Pillow-8.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a"}, 400 | {file = "Pillow-8.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8"}, 401 | {file = "Pillow-8.0.1-cp37-cp37m-win32.whl", hash = "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0"}, 402 | {file = "Pillow-8.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039"}, 403 | {file = "Pillow-8.0.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11"}, 404 | {file = "Pillow-8.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72"}, 405 | {file = "Pillow-8.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792"}, 406 | {file = "Pillow-8.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015"}, 407 | {file = "Pillow-8.0.1-cp38-cp38-win32.whl", hash = "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271"}, 408 | {file = "Pillow-8.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7"}, 409 | {file = "Pillow-8.0.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5"}, 410 | {file = "Pillow-8.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce"}, 411 | {file = "Pillow-8.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3"}, 412 | {file = "Pillow-8.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544"}, 413 | {file = "Pillow-8.0.1-cp39-cp39-win32.whl", hash = "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140"}, 414 | {file = "Pillow-8.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021"}, 415 | {file = "Pillow-8.0.1-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6"}, 416 | {file = "Pillow-8.0.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb"}, 417 | {file = "Pillow-8.0.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8"}, 418 | {file = "Pillow-8.0.1.tar.gz", hash = "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e"}, 419 | ] 420 | pyparsing = [ 421 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 422 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 423 | ] 424 | python-dateutil = [ 425 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 426 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 427 | ] 428 | pytz = [ 429 | {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, 430 | {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, 431 | ] 432 | six = [ 433 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, 434 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, 435 | ] 436 | wordcloud = [ 437 | {file = "wordcloud-1.8.1-cp27-cp27m-macosx_10_6_x86_64.whl", hash = "sha256:2e85b1a6b8211436d47f902837c866c682c5d00046706f1d8e72cc0cd9210d64"}, 438 | {file = "wordcloud-1.8.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:e0d3f03e4f27faf543859b4bcd229d320361d065cf4786f149081adea4ee68e8"}, 439 | {file = "wordcloud-1.8.1-cp27-cp27m-win32.whl", hash = "sha256:a02b27400bc4b2e4efdd8377e8cd138496b6b6e15a082d9c7b7c7b74a9ee8a55"}, 440 | {file = "wordcloud-1.8.1-cp27-cp27m-win_amd64.whl", hash = "sha256:6450f67c207f2ab4513c4aac803226427b39f7fc8d3498c0b41a03834b2fc426"}, 441 | {file = "wordcloud-1.8.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f49f5213e978fb5d8ff8f6e6067de657e81ca863cab549b198579f58e1430e7c"}, 442 | {file = "wordcloud-1.8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:41dafafe3768675ba8285d21ac68bd53191b50da85aba2a7fb35964d1649156f"}, 443 | {file = "wordcloud-1.8.1-cp36-cp36m-win32.whl", hash = "sha256:cd8c44a21a3207da813c7b7e486ed41fe517eb7b8535deef945437f4cea2b245"}, 444 | {file = "wordcloud-1.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2d5f63cf1f65126d6d1b8d55acae15961c5bd55e6ef8978bf269831dafcd1408"}, 445 | {file = "wordcloud-1.8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6ff0ca777c801cd2a38deef30019f02fe35df1be9c31f6c301a34bd45006e26b"}, 446 | {file = "wordcloud-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:9ed09d2d2916514845e42b0cc5dd86f65d4f0f89ab4bf164cc9a628d9db5a76b"}, 447 | {file = "wordcloud-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2e1fc5991f52a0e191873025f5d8cf852eb1c39f2579d43e6d2589dc04d620d9"}, 448 | {file = "wordcloud-1.8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:daa7d72f0004fcfea5f649f1683dbe47c4a9c5f721aabeb41278a4ee33f21424"}, 449 | {file = "wordcloud-1.8.1-cp38-cp38-win32.whl", hash = "sha256:a4a626df47eacec207988e1ffdb5bd16b2b62ac83ea2442a0043080c0c5103ac"}, 450 | {file = "wordcloud-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f190eb3efcc17765a4ed9d78001fdb713988886b93e54b2758fcf4f6c519ad51"}, 451 | {file = "wordcloud-1.8.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:90e9cff2c6939b5e1fa87d2a2c5d79c8ac2eafc4c9b9bc1bcb865ca5fde7e758"}, 452 | {file = "wordcloud-1.8.1.tar.gz", hash = "sha256:e6ef771aac17c1cf8558c8d5ef025796184066d7b78f8118aefe011fb0d22952"}, 453 | ] 454 | wurlitzer = [ 455 | {file = "wurlitzer-2.0.1-py2.py3-none-any.whl", hash = "sha256:2b4f1a6b62660ccddb066a49cda3845f6583ed5ab70e22a0ccc0f741063945d8"}, 456 | {file = "wurlitzer-2.0.1.tar.gz", hash = "sha256:d08f0728a998441aac3d7a0f8cd6dfed2ba2525144878fb49599b719085b7543"}, 457 | ] 458 | --------------------------------------------------------------------------------