amueller: This PR only adds the flag, but keeps the default as "true", right?
That seems like a good solution to me. The program is more "here is some text, make a word-cloud out of it". And separating two words that always appear together isn't that intuitive to me.
cjmay: Okay, I'll change it: the default behavior in the CLI would be switched in the current PR.
amueller: thanks :)
diff --git a/wordcloud/wordcloud.py b/wordcloud/wordcloud.py
index e63d922..ae5d107 100644
--- a/wordcloud/wordcloud.py
+++ b/wordcloud/wordcloud.py
@@ -348,11 +348,7 @@ class WordCloud(object):
         """
         # make sure frequencies are sorted and normalized
         frequencies = sorted(frequencies.items(), key=item1, reverse=True)
-        if len(frequencies) <= 0:
-            raise ValueError("We need at least 1 word to plot a word cloud, "
-                             "got %d." % len(frequencies))
         frequencies = frequencies[:self.max_words]
-
         # largest entry will be 1
         max_frequency = float(frequencies[0][1])
 
@@ -364,6 +360,10 @@ class WordCloud(object):
         else:
             random_state = Random()
 
+        if len(frequencies) <= 0:
+            print("We need at least 1 word to plot a word cloud, got %d."
+                  % len(frequencies))
+
         if self.mask is not None:
             mask = self.mask
             width = mask.shape[1]
diff --git a/wordcloud/wordcloud_cli.py b/wordcloud/wordcloud_cli.py
index 2a54deb..cc105fe 100644
--- a/wordcloud/wordcloud_cli.py
+++ b/wordcloud/wordcloud_cli.py
@@ -16,7 +16,8 @@ def main(args):
     wordcloud = wc.WordCloud(stopwords=args.stopwords, mask=args.mask,
         width=args.width, height=args.height, font_path=args.font_path,
         margin=args.margin, relative_scaling=args.relative_scaling,
-        color_func=args.color_func, background_color=args.background_color).generate(args.text)
+        color_func=args.color_func, background_color=args.background_color,
+        collocations=args.collocations).generate(args.text)
     image = wordcloud.to_image()
 
     with args.imagefile:
@@ -51,6 +52,8 @@ def parse_args(arguments):
         help='use given color as coloring for the image - accepts any value from PIL.ImageColor.getcolor')
     parser.add_argument('--background', metavar='color', default='black', type=str, dest='background_color',
         help='use given color as background color for the image - accepts any value from PIL.ImageColor.getcolor')
+    parser.add_argument('--no_collocations', action='store_true',
+        help='do not add collocations (bigrams) to word cloud (default: add unigrams and bigrams)')
     args = parser.parse_args(arguments)
 
     if args.colormask and args.color:
@@ -74,6 +77,8 @@ def parse_args(arguments):
     if args.color:
         color_func = wc.get_single_color_func(args.color)
 
+    args.collocations = not args.no_collocations
+
     args.color_func = color_func
     return args
 

Some text distributions yield duplicate words in image
As a workaround for #226 I tried generating text according to the desired distribution: [food.txt](https://github.com/amueller/word_cloud/files/858596/food.txt)

Note in particular there are few word types but relatively high number of tokens per type compared to natural language.  Tokens of each word type are also grouped.  (My guess would be one or both of these properties are causing the issue.)

However, `wordcloud_cli.py` (from master) on this generated text file, with default options, yielded a word cloud with each word duplicated conspicuously: ![food](https://cloud.githubusercontent.com/assets/457238/24154620/126cef40-0e28-11e7-8946-4e6284ceffae.png)

diff --git a/test/test_wordcloud.py b/test/test_wordcloud.py
index 2d2662b..ee91f2e 100644
--- a/test/test_wordcloud.py
+++ b/test/test_wordcloud.py
@@ -76,16 +76,6 @@ def test_multiple_s():
     assert_in("flosss", wc.words_)
 
 
-def test_empty_text():
-    # test originally empty text raises an exception
-    wc = WordCloud(stopwords=[])
-    assert_raises(ValueError, wc.generate, '')
-
-    # test empty-after-filtering text raises an exception
-    wc = WordCloud(stopwords=['a', 'b'])
-    assert_raises(ValueError, wc.generate, 'a b a')
-
-
 def test_default():
     # test that default word cloud creation and conversions work
     wc = WordCloud(max_words=50)
diff --git a/test/test_wordcloud_cli.py b/test/test_wordcloud_cli.py
index bdfef88..d350d81 100644
--- a/test/test_wordcloud_cli.py
+++ b/test/test_wordcloud_cli.py
@@ -17,7 +17,10 @@ ARGUMENT_SPEC_TYPED = [
     ArgOption(cli_name='width', init_name='width', pass_value=13, fail_value=1.),
     ArgOption(cli_name='height', init_name='height', pass_value=15, fail_value=1.),
     ArgOption(cli_name='margin', init_name='margin', pass_value=17, fail_value=1.),
-    ArgOption(cli_name='relative_scaling', init_name='relative_scaling', pass_value=1, fail_value='c')
+    ArgOption(cli_name='relative_scaling', init_name='relative_scaling', pass_value=1, fail_value='c'),
+]
+ARGUMENT_SPEC_UNARY = [
+    ArgOption(cli_name='no_collocations', init_name='collocations', pass_value=True, fail_value=1)
 ]
 ARGUMENT_SPEC_REMAINING = [
     ArgOption(cli_name='stopwords', init_name='stopwords', pass_value=temp.name, fail_value=None),
@@ -26,10 +29,12 @@ ARGUMENT_SPEC_REMAINING = [
     ArgOption(cli_name='color', init_name='color_func', pass_value='red', fail_value=None),
     ArgOption(cli_name='background', init_name='background_color', pass_value='grey', fail_value=None)
 ]
+ARGUMENT_CLI_NAMES_UNARY = [arg_opt.cli_name for arg_opt in ARGUMENT_SPEC_UNARY]
 
 def all_arguments():
     arguments = []
     arguments.extend(ARGUMENT_SPEC_TYPED)
+    arguments.extend(ARGUMENT_SPEC_UNARY)
     arguments.extend(ARGUMENT_SPEC_REMAINING)
     return arguments
 
@@ -56,6 +61,13 @@ def check_argument(name, result_name, value):
     assert_in(result_name, vars(args))
 
 
+def check_argument_unary(name, result_name):
+    text = NamedTemporaryFile()
+
+    args = cli.parse_args(['--text', text.name, '--' + name])
+    assert_in(result_name, vars(args))
+
+
 def check_argument_type(name, value):
     text = NamedTemporaryFile()
 
@@ -71,7 +83,9 @@ def check_argument_type(name, value):
 
 def test_parse_args_are_passed_along():
     for option in all_arguments():
-        if option.cli_name != 'mask':
+        if option.cli_name in ARGUMENT_CLI_NAMES_UNARY:
+            yield check_argument_unary, option.cli_name, option.init_name
+        elif option.cli_name != 'mask':
             yield check_argument, option.cli_name, option.init_name, option.pass_value
 
 

pip install -e .
parse_log_pytest
pytest
nose mock pytest
apt-get update apt-get install -y gcc g++
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
cycler==0.11.0
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
kiwisolver==1.3.1
matplotlib==3.3.4
mock==5.2.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
nose==1.3.7
numpy==1.19.5
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
Pillow==8.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
python-dateutil==2.9.0.post0
six==1.17.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
-e git+https://github.com/amueller/word_cloud.git@2b868941a71e0ad6efac3b25433b97e8776e381b#egg=wordcloud
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: word_cloud
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cycler==0.11.0
      - kiwisolver==1.3.1
      - matplotlib==3.3.4
      - mock==5.2.0
      - nose==1.3.7
      - numpy==1.19.5
      - pillow==8.4.0
      - python-dateutil==2.9.0.post0
      - six==1.17.0
prefix: /opt/conda/envs/word_cloud

test/test_wordcloud_cli.py::test_main_passes_arguments_through
test/test_wordcloud.py::test_collocations test/test_wordcloud.py::test_plurals_numbers test/test_wordcloud.py::test_multiple_s test/test_wordcloud.py::test_default test/test_wordcloud.py::test_stopwords_lowercasing test/test_wordcloud.py::test_writing_to_file test/test_wordcloud.py::test_check_errors test/test_wordcloud.py::test_recolor test/test_wordcloud.py::test_random_state test/test_wordcloud.py::test_mask test/test_wordcloud.py::test_single_color_func test/test_wordcloud.py::test_single_color_func_grey test/test_wordcloud.py::test_process_text test/test_wordcloud.py::test_generate_from_frequencies test/test_wordcloud.py::test_relative_scaling_zero test/test_wordcloud_cli.py::test_check_duplicate_color_error test/test_wordcloud_cli.py::test_parse_args_defaults_to_random_color test/test_wordcloud_cli.py::test_cli_writes_image
wordcloud/wordcloud.py wordcloud/wordcloud_cli.py
wordcloud/wordcloud.py wordcloud/wordcloud_cli.py