Repository: piskvorky/gensim

Issue:
D2VTransformer raises if passed a Pandas series without index key 0
`D2VTransformer` raises if passed a Pandas series with an index that does not contain the key 0:

```python
import pandas as pd
from gensim.sklearn_api import D2VTransformer
from gensim.test.utils import common_texts

series = pd.Series(common_texts)
series.index += 1  # Increment the index so that it does not contain the key 0

transformer = D2VTransformer(min_count=1, size=5)
transformer.fit(series)
```

Output:

```python
﻿Traceback (most recent call last):
  File "main.py", line 9, in <module>
    transformer.fit(series)
  File "venv/lib/python3.7/site-packages/gensim/sklearn_api/d2vmodel.py", line 162, in fit
    if isinstance(X[0], doc2vec.TaggedDocument):
  File "venv/lib/python3.7/site-packages/pandas/core/series.py", line 868, in __getitem__
    result = self.index.get_value(self, key)
  File "venv/lib/python3.7/site-packages/pandas/core/indexes/base.py", line 4375, in get_value
    tz=getattr(series.dtype, 'tz', None))
  File "pandas/_libs/index.pyx", line 81, in pandas._libs.index.IndexEngine.get_value
  File "pandas/_libs/index.pyx", line 89, in pandas._libs.index.IndexEngine.get_value
  File "pandas/_libs/index.pyx", line 132, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 987, in pandas._libs.hashtable.Int64HashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 993, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: 0
```

This occurs because the [`fit`](https://github.com/RaRe-Technologies/gensim/blob/4543646d3fe3496e11bc935e72cbf9b18504442e/gensim/sklearn_api/d2vmodel.py#L162) and [`transform`](https://github.com/RaRe-Technologies/gensim/blob/4543646d3fe3496e11bc935e72cbf9b18504442e/gensim/sklearn_api/d2vmodel.py#L198) methods of `D2VTransformer` require `__getitem__` on the passed iterable not to raise an exception for key 0.

Versions:

Darwin-18.6.0-x86_64-i386-64bit
Python 3.7.3 (default, Mar 27 2019, 09:23:15) [Clang 10.0.1 (clang-1001.0.46.3)]
NumPy 1.16.4
SciPy 1.3.0
gensim 3.8.0
FAST_VERSION 1


Discussion:
TODO: check other `sklearn_api` models too – same issue there as in `D2VTransformer`?
So I need to check [here](https://github.com/RaRe-Technologies/gensim/blob/2131e3a3b0231c7eb0b0b447539d2ab7c8802633/gensim/sklearn_api/d2vmodel.py#L162) for the first element, not element at index 0?
Will that be enough to close the issue?

Resolving patch:
diff --git a/gensim/sklearn_api/d2vmodel.py b/gensim/sklearn_api/d2vmodel.py
--- a/gensim/sklearn_api/d2vmodel.py
+++ b/gensim/sklearn_api/d2vmodel.py
@@ -159,7 +159,7 @@ def fit(self, X, y=None):
             The trained model.
 
         """
-        if isinstance(X[0], doc2vec.TaggedDocument):
+        if isinstance([i for i in X[:1]][0], doc2vec.TaggedDocument):
             d2v_sentences = X
         else:
             d2v_sentences = [doc2vec.TaggedDocument(words, [i]) for i, words in enumerate(X)]


Test patch:
diff --git a/gensim/test/test_d2vmodel.py b/gensim/test/test_d2vmodel.py
new file mode 100644
--- /dev/null
+++ b/gensim/test/test_d2vmodel.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2010 Radim Rehurek <radimrehurek@seznam.cz>
+# Licensed under the GNU LGPL v2.1 - http://www.gnu.org/licenses/lgpl.html
+
+"""
+Automated tests for checking D2VTransformer class.
+"""
+
+import unittest
+import logging
+from gensim.sklearn_api import D2VTransformer
+from gensim.test.utils import common_texts
+
+
+class IteratorForIterable:
+    """Iterator capable of folding into list."""
+    def __init__(self, iterable):
+        self._data = iterable
+        self._index = 0
+
+    def __next__(self):
+        if len(self._data) > self._index:
+            result = self._data[self._index]
+            self._index += 1
+            return result
+        raise StopIteration
+
+
+class IterableWithoutZeroElement:
+    """
+    Iterable, emulating pandas.Series behaviour without 0-th element.
+    Equivalent to calling `series.index += 1`.
+    """
+    def __init__(self, data):
+        self.data = data
+
+    def __getitem__(self, key):
+        if key == 0:
+            raise KeyError("Emulation of absence of item with key 0.")
+        return self.data[key]
+
+    def __iter__(self):
+        return IteratorForIterable(self.data)
+
+
+class TestD2VTransformer(unittest.TestCase):
+    def TestWorksWithIterableNotHavingElementWithZeroIndex(self):
+        a = IterableWithoutZeroElement(common_texts)
+        transformer = D2VTransformer(min_count=1, size=5)
+        transformer.fit(a)
+
+
+if __name__ == '__main__':
+    logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.DEBUG)
+    unittest.main()