We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] Date Train-Test Leakage displays datetimes as object, should be human readable
![image](https://user-images.githubusercontent.com/42312361/147881742-edd663f8-18c7-4a14-903c-77b21bf76d17.png)

output of DateTrainTestLeakage is currently `datetime.datetime(...)` should be `1992/02/13 13:23....` instead

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `deepchecks/utils/strings.py`
Content:
```
1 # ----------------------------------------------------------------------------
2 # Copyright (C) 2021 Deepchecks (https://www.deepchecks.com)
3 #
4 # This file is part of Deepchecks.
5 # Deepchecks is distributed under the terms of the GNU Affero General
6 # Public License (version 3 or later).
7 # You should have received a copy of the GNU Affero General Public License
8 # along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.
9 # ----------------------------------------------------------------------------
10 #
11 """String functions."""
12 import random
13 from string import ascii_uppercase, digits
14 import typing as t
15 import re
16 from collections import defaultdict
17 from decimal import Decimal
18 from copy import copy
19 
20 import pandas as pd
21 from pandas.core.dtypes.common import is_numeric_dtype
22 
23 from deepchecks.utils.typing import Hashable
24 from deepchecks.utils.validation import ensure_hashable_or_mutable_sequence
25 
26 
27 __all__ = [
28     'string_baseform',
29     'get_base_form_to_variants_dict',
30     'split_camel_case',
31     'split_and_keep',
32     'split_by_order',
33     'is_string_column',
34     'format_percent',
35     'format_number',
36     'format_list',
37     'format_columns_for_condition',
38     'get_random_string'
39 ]
40 
41 
42 def get_random_string(n: int = 5):
43     """Return random string at the given size.
44 
45     Args:
46         n (int): the size of the string to return.
47     Returns:
48         (str): a random string
49     """
50     return ''.join(random.choices(ascii_uppercase + digits, k=n))
51 
52 
53 def string_baseform(string: str) -> str:
54     """Remove special characters from given string, leaving only a-z, A-Z, 0-9 characters.
55 
56     Args:
57         string (str): string to remove special characters from
58 
59     Returns:
60         (str): string without special characters
61     """
62     if not isinstance(string, str):
63         return string
64     return re.sub('[^A-Za-z0-9]+', '', string).lower()
65 
66 
67 def is_string_column(column: pd.Series) -> bool:
68     """Determine whether a pandas series is string type."""
69     if is_numeric_dtype(column):
70         return False
71     try:
72         pd.to_numeric(column)
73         return False
74     except ValueError:
75         return True
76     # Non string objects like pd.Timestamp results in TypeError
77     except TypeError:
78         return False
79 
80 
81 def split_camel_case(string: str) -> str:
82     """Split string where there are capital letters and enter space instead.
83 
84     Args:
85         string (str): string to change
86     """
87     return ' '.join(re.findall('[A-Z][^A-Z]*', string))
88 
89 
90 def get_base_form_to_variants_dict(uniques: t.Iterable[str]) -> t.Dict[str, t.Set[str]]:
91     """Create dict of base-form of the uniques to their values.
92 
93     function gets a set of strings, and returns a dictionary of shape Dict[str, Set]
94     the key being the "base_form" (a clean version of the string),
95     and the value being a set of all existing original values.
96     This is done using the StringCategory class.
97     """
98     base_form_to_variants = defaultdict(set)
99     for item in uniques:
100         base_form_to_variants[string_baseform(item)].add(item)
101     return base_form_to_variants
102 
103 
104 def str_min_find(s: str, substr_list: t.Iterable[str]) -> t.Tuple[int, str]:
105     """
106     Find the minimal first occurence of a substring in a string, and return both the index and substring.
107 
108     Args:
109         s (str): The string in which we look for substrings
110         substr_list: list of substrings to find
111 
112     Returns:
113         min_find (int): index of minimal first occurence of substring
114         min_substr (str): the substring that occures in said index
115 
116     """
117     min_find = -1
118     min_substr = ''
119     for substr in substr_list:
120         first_find = s.find(substr)
121         if first_find != -1 and (first_find < min_find or min_find == -1):
122             min_find = first_find
123             min_substr = substr
124     return min_find, min_substr
125 
126 
127 def split_and_keep(s: str, separators: t.Union[str, t.Iterable[str]]) -> t.List[str]:
128     """
129     Split string by a another substring into a list. Like str.split(), but keeps the separator occurrences in the list.
130 
131     Args:
132         s (str): the string to split
133         separators (str): the substring to split by
134 
135     Returns:
136         List[str]: list of substrings, including the separator occurrences in string
137 
138     """
139     if isinstance(separators, str):
140         separators = [separators]
141 
142     split_s = []
143     while len(s) != 0:
144         i, substr = str_min_find(s=s, substr_list=separators)
145         if i == 0:
146             split_s.append(substr)
147             s = s[len(substr):]
148         elif i == -1:
149             split_s.append(s)
150             break
151         else:
152             pre, _ = s.split(substr, 1)
153             split_s.append(pre)
154             s = s[len(pre):]
155     return split_s
156 
157 
158 def split_by_order(s: str, separators: t.Iterable[str], keep: bool = True) -> t.List[str]:
159     """
160     Split string by a a list of substrings, each used once as a separator.
161 
162     Args:
163         s (str): the string to split
164         separators (List[str]): list of substrings to split by
165         keep (bool): whether to keep the separators in list as well. Default is True.
166 
167     Returns:
168         List[str]: list of substrings
169     """
170     split_s = []
171     separators = list(copy(separators))
172     while len(s) != 0:
173         if len(separators) > 0:
174             sep = separators[0]
175             if s.find(sep) == 0:
176                 if keep is True:
177                     split_s.append(sep)
178                 s = s[len(sep):]
179                 separators.pop(0)
180             else:
181                 pre, _ = s.split(sep, 1)
182                 split_s.append(pre)
183                 s = s[len(pre):]
184         else:
185             split_s.append(s)
186             break
187     return split_s
188 
189 
190 def format_percent(ratio: float, floating_point: int = 2) -> str:
191     """Format percent for elegant display.
192 
193     Args:
194         ratio (float): Ratio to be displayed as percent
195         floating_point (int): Number of floating points to display
196 
197     Returns:
198         String of ratio as percent
199     """
200     result: str
201     if ratio < 0:
202         ratio = -ratio
203         prefix = '-'
204     else:
205         prefix = ''
206 
207     if int(ratio) == ratio:
208         result = f'{int(ratio) * 100}%'
209     elif ratio > 1:
210         result = f'{ratio * 100:.{floating_point}f}%'
211     elif ratio < 10**(-(2+floating_point)):
212         result = f'{Decimal(ratio * 100):.{floating_point}E}%'
213     elif ratio > (1-10**(-(2+floating_point))):
214         if floating_point > 0:
215             result = f'99.{"".join(["9"]*floating_point)}%'
216         else:
217             result = '99%'
218     else:
219         result = f'{ratio:.{floating_point}%}'
220 
221     return prefix + result
222 
223 
224 def format_number(x, floating_point: int = 2) -> str:
225     """Format number for elegant display.
226 
227     Args:
228         x (): Number to be displayed
229         floating_point (int): Number of floating points to display
230 
231     Returns:
232         String of beautified number
233     """
234     def add_commas(x):
235         return f'{x:,}'  # yes this actually formats the number 1000 to "1,000"
236 
237     # 0 is lost in the next if case, so we have it here as a special use-case
238     if x == 0:
239         return '0'
240 
241     # If x is a very small number, that would be rounded to 0, we would prefer to return it as the format 1.0E-3.
242     if abs(x) < 10 ** (-floating_point):
243         return f'{Decimal(x):.{floating_point}E}'
244 
245     # If x is an integer, or if x when rounded is an integer (e.g. 1.999999), then return as integer:
246     if round(x) == round(x, floating_point):
247         return add_commas(round(x))
248 
249     # If not, return as a float, but don't print unnecessary zeros at end:
250     else:
251         ret_x = round(x, floating_point)
252         return add_commas(ret_x).rstrip('0')
253 
254 
255 def format_list(l: t.List[Hashable], max_elements_to_show: int = 10, max_string_length: int = 40) -> str:
256     """Format columns properties for display in condition name.
257 
258     Args:
259         l (List): list to print.
260         max_elements_to_show (int): max elemnts to print before terminating.
261         max_string_length (int): max string length before terminating.
262 
263     Return:
264         String of beautified list
265     """
266     string_list = [str(i) for i in l[:max_elements_to_show]]
267     output = ', '.join(string_list)
268 
269     if len(output) > max_string_length:
270         return output[:max_string_length] + '...'
271 
272     if len(l) > max_elements_to_show:
273         return output + ', ...'
274 
275     return output
276 
277 
278 def format_columns_for_condition(
279     columns: t.Union[Hashable, t.List[Hashable], None] = None,
280     ignore_columns: t.Union[Hashable, t.List[Hashable], None] = None
281 ) -> str:
282     """Format columns properties for display in condition name.
283 
284     Args:
285         columns (Union[Hashable, List[Hashable], None]):
286             columns to include into resulting string
287         ignore_columns (Union[Hashable, List[Hashable], None]):
288             columns to not include into resulting string
289 
290     Returns: formatted string of columns
291     """
292     if columns is not None:
293         columns = ensure_hashable_or_mutable_sequence(columns)
294         return f'columns: {",".join(map(str, columns))}'
295     elif ignore_columns is not None:
296         ignore_columns = ensure_hashable_or_mutable_sequence(ignore_columns)
297         return f'all columns ignoring: {",".join(map(str, ignore_columns))}'
298     else:
299         return 'all columns'
300
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/deepchecks/utils/strings.py b/deepchecks/utils/strings.py
--- a/deepchecks/utils/strings.py
+++ b/deepchecks/utils/strings.py
@@ -10,9 +10,10 @@
 #
 """String functions."""
 import random
-from string import ascii_uppercase, digits
 import typing as t
 import re
+from datetime import datetime
+from string import ascii_uppercase, digits
 from collections import defaultdict
 from decimal import Decimal
 from copy import copy
@@ -35,7 +36,8 @@
     'format_number',
     'format_list',
     'format_columns_for_condition',
-    'get_random_string'
+    'get_random_string',
+    'format_datetime'
 ]
 
 
@@ -297,3 +299,27 @@
         return f'all columns ignoring: {",".join(map(str, ignore_columns))}'
     else:
         return 'all columns'
+
+
+def format_datetime(
+    value,
+    datetime_format: str = '%Y/%m/%d %H:%M:%S.%f %Z%z'  # # 1992/02/13 13:23:00 UTC+0000
+) -> str:
+    """Format datetime object or timestamp value.
+
+    Args:
+        value (Union[datetime, int, float]): datetime (timestamp) to format
+        format (str): format to use
+
+    Returns:
+        str: string representation of the provided value
+
+    Raises:
+        ValueError: if unexpected value type was passed to the function
+    """
+    if isinstance(value, datetime):
+        return value.strftime(datetime_format)
+    elif isinstance(value, (int, float)):
+        return datetime.fromtimestamp(value).strftime(datetime_format)
+    else:
+        raise ValueError(f'Unsupported value type - {type(value).__name__}')

{"golden_diff": "diff --git a/deepchecks/utils/strings.py b/deepchecks/utils/strings.py\n--- a/deepchecks/utils/strings.py\n+++ b/deepchecks/utils/strings.py\n@@ -10,9 +10,10 @@\n #\n \"\"\"String functions.\"\"\"\n import random\n-from string import ascii_uppercase, digits\n import typing as t\n import re\n+from datetime import datetime\n+from string import ascii_uppercase, digits\n from collections import defaultdict\n from decimal import Decimal\n from copy import copy\n@@ -35,7 +36,8 @@\n     'format_number',\n     'format_list',\n     'format_columns_for_condition',\n-    'get_random_string'\n+    'get_random_string',\n+    'format_datetime'\n ]\n \n \n@@ -297,3 +299,27 @@\n         return f'all columns ignoring: {\",\".join(map(str, ignore_columns))}'\n     else:\n         return 'all columns'\n+\n+\n+def format_datetime(\n+    value,\n+    datetime_format: str = '%Y/%m/%d %H:%M:%S.%f %Z%z'  # # 1992/02/13 13:23:00 UTC+0000\n+) -> str:\n+    \"\"\"Format datetime object or timestamp value.\n+\n+    Args:\n+        value (Union[datetime, int, float]): datetime (timestamp) to format\n+        format (str): format to use\n+\n+    Returns:\n+        str: string representation of the provided value\n+\n+    Raises:\n+        ValueError: if unexpected value type was passed to the function\n+    \"\"\"\n+    if isinstance(value, datetime):\n+        return value.strftime(datetime_format)\n+    elif isinstance(value, (int, float)):\n+        return datetime.fromtimestamp(value).strftime(datetime_format)\n+    else:\n+        raise ValueError(f'Unsupported value type - {type(value).__name__}')\n", "issue": "[BUG] Date Train-Test Leakage displays datetimes as object, should be human readable\n![image](https://user-images.githubusercontent.com/42312361/147881742-edd663f8-18c7-4a14-903c-77b21bf76d17.png)\r\n\r\noutput of DateTrainTestLeakage is currently `datetime.datetime(...)` should be `1992/02/13 13:23....` instead\n", "before_files": [{"content": "# ----------------------------------------------------------------------------\n# Copyright (C) 2021 Deepchecks (https://www.deepchecks.com)\n#\n# This file is part of Deepchecks.\n# Deepchecks is distributed under the terms of the GNU Affero General\n# Public License (version 3 or later).\n# You should have received a copy of the GNU Affero General Public License\n# along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.\n# ----------------------------------------------------------------------------\n#\n\"\"\"String functions.\"\"\"\nimport random\nfrom string import ascii_uppercase, digits\nimport typing as t\nimport re\nfrom collections import defaultdict\nfrom decimal import Decimal\nfrom copy import copy\n\nimport pandas as pd\nfrom pandas.core.dtypes.common import is_numeric_dtype\n\nfrom deepchecks.utils.typing import Hashable\nfrom deepchecks.utils.validation import ensure_hashable_or_mutable_sequence\n\n\n__all__ = [\n    'string_baseform',\n    'get_base_form_to_variants_dict',\n    'split_camel_case',\n    'split_and_keep',\n    'split_by_order',\n    'is_string_column',\n    'format_percent',\n    'format_number',\n    'format_list',\n    'format_columns_for_condition',\n    'get_random_string'\n]\n\n\ndef get_random_string(n: int = 5):\n    \"\"\"Return random string at the given size.\n\n    Args:\n        n (int): the size of the string to return.\n    Returns:\n        (str): a random string\n    \"\"\"\n    return ''.join(random.choices(ascii_uppercase + digits, k=n))\n\n\ndef string_baseform(string: str) -> str:\n    \"\"\"Remove special characters from given string, leaving only a-z, A-Z, 0-9 characters.\n\n    Args:\n        string (str): string to remove special characters from\n\n    Returns:\n        (str): string without special characters\n    \"\"\"\n    if not isinstance(string, str):\n        return string\n    return re.sub('[^A-Za-z0-9]+', '', string).lower()\n\n\ndef is_string_column(column: pd.Series) -> bool:\n    \"\"\"Determine whether a pandas series is string type.\"\"\"\n    if is_numeric_dtype(column):\n        return False\n    try:\n        pd.to_numeric(column)\n        return False\n    except ValueError:\n        return True\n    # Non string objects like pd.Timestamp results in TypeError\n    except TypeError:\n        return False\n\n\ndef split_camel_case(string: str) -> str:\n    \"\"\"Split string where there are capital letters and enter space instead.\n\n    Args:\n        string (str): string to change\n    \"\"\"\n    return ' '.join(re.findall('[A-Z][^A-Z]*', string))\n\n\ndef get_base_form_to_variants_dict(uniques: t.Iterable[str]) -> t.Dict[str, t.Set[str]]:\n    \"\"\"Create dict of base-form of the uniques to their values.\n\n    function gets a set of strings, and returns a dictionary of shape Dict[str, Set]\n    the key being the \"base_form\" (a clean version of the string),\n    and the value being a set of all existing original values.\n    This is done using the StringCategory class.\n    \"\"\"\n    base_form_to_variants = defaultdict(set)\n    for item in uniques:\n        base_form_to_variants[string_baseform(item)].add(item)\n    return base_form_to_variants\n\n\ndef str_min_find(s: str, substr_list: t.Iterable[str]) -> t.Tuple[int, str]:\n    \"\"\"\n    Find the minimal first occurence of a substring in a string, and return both the index and substring.\n\n    Args:\n        s (str): The string in which we look for substrings\n        substr_list: list of substrings to find\n\n    Returns:\n        min_find (int): index of minimal first occurence of substring\n        min_substr (str): the substring that occures in said index\n\n    \"\"\"\n    min_find = -1\n    min_substr = ''\n    for substr in substr_list:\n        first_find = s.find(substr)\n        if first_find != -1 and (first_find < min_find or min_find == -1):\n            min_find = first_find\n            min_substr = substr\n    return min_find, min_substr\n\n\ndef split_and_keep(s: str, separators: t.Union[str, t.Iterable[str]]) -> t.List[str]:\n    \"\"\"\n    Split string by a another substring into a list. Like str.split(), but keeps the separator occurrences in the list.\n\n    Args:\n        s (str): the string to split\n        separators (str): the substring to split by\n\n    Returns:\n        List[str]: list of substrings, including the separator occurrences in string\n\n    \"\"\"\n    if isinstance(separators, str):\n        separators = [separators]\n\n    split_s = []\n    while len(s) != 0:\n        i, substr = str_min_find(s=s, substr_list=separators)\n        if i == 0:\n            split_s.append(substr)\n            s = s[len(substr):]\n        elif i == -1:\n            split_s.append(s)\n            break\n        else:\n            pre, _ = s.split(substr, 1)\n            split_s.append(pre)\n            s = s[len(pre):]\n    return split_s\n\n\ndef split_by_order(s: str, separators: t.Iterable[str], keep: bool = True) -> t.List[str]:\n    \"\"\"\n    Split string by a a list of substrings, each used once as a separator.\n\n    Args:\n        s (str): the string to split\n        separators (List[str]): list of substrings to split by\n        keep (bool): whether to keep the separators in list as well. Default is True.\n\n    Returns:\n        List[str]: list of substrings\n    \"\"\"\n    split_s = []\n    separators = list(copy(separators))\n    while len(s) != 0:\n        if len(separators) > 0:\n            sep = separators[0]\n            if s.find(sep) == 0:\n                if keep is True:\n                    split_s.append(sep)\n                s = s[len(sep):]\n                separators.pop(0)\n            else:\n                pre, _ = s.split(sep, 1)\n                split_s.append(pre)\n                s = s[len(pre):]\n        else:\n            split_s.append(s)\n            break\n    return split_s\n\n\ndef format_percent(ratio: float, floating_point: int = 2) -> str:\n    \"\"\"Format percent for elegant display.\n\n    Args:\n        ratio (float): Ratio to be displayed as percent\n        floating_point (int): Number of floating points to display\n\n    Returns:\n        String of ratio as percent\n    \"\"\"\n    result: str\n    if ratio < 0:\n        ratio = -ratio\n        prefix = '-'\n    else:\n        prefix = ''\n\n    if int(ratio) == ratio:\n        result = f'{int(ratio) * 100}%'\n    elif ratio > 1:\n        result = f'{ratio * 100:.{floating_point}f}%'\n    elif ratio < 10**(-(2+floating_point)):\n        result = f'{Decimal(ratio * 100):.{floating_point}E}%'\n    elif ratio > (1-10**(-(2+floating_point))):\n        if floating_point > 0:\n            result = f'99.{\"\".join([\"9\"]*floating_point)}%'\n        else:\n            result = '99%'\n    else:\n        result = f'{ratio:.{floating_point}%}'\n\n    return prefix + result\n\n\ndef format_number(x, floating_point: int = 2) -> str:\n    \"\"\"Format number for elegant display.\n\n    Args:\n        x (): Number to be displayed\n        floating_point (int): Number of floating points to display\n\n    Returns:\n        String of beautified number\n    \"\"\"\n    def add_commas(x):\n        return f'{x:,}'  # yes this actually formats the number 1000 to \"1,000\"\n\n    # 0 is lost in the next if case, so we have it here as a special use-case\n    if x == 0:\n        return '0'\n\n    # If x is a very small number, that would be rounded to 0, we would prefer to return it as the format 1.0E-3.\n    if abs(x) < 10 ** (-floating_point):\n        return f'{Decimal(x):.{floating_point}E}'\n\n    # If x is an integer, or if x when rounded is an integer (e.g. 1.999999), then return as integer:\n    if round(x) == round(x, floating_point):\n        return add_commas(round(x))\n\n    # If not, return as a float, but don't print unnecessary zeros at end:\n    else:\n        ret_x = round(x, floating_point)\n        return add_commas(ret_x).rstrip('0')\n\n\ndef format_list(l: t.List[Hashable], max_elements_to_show: int = 10, max_string_length: int = 40) -> str:\n    \"\"\"Format columns properties for display in condition name.\n\n    Args:\n        l (List): list to print.\n        max_elements_to_show (int): max elemnts to print before terminating.\n        max_string_length (int): max string length before terminating.\n\n    Return:\n        String of beautified list\n    \"\"\"\n    string_list = [str(i) for i in l[:max_elements_to_show]]\n    output = ', '.join(string_list)\n\n    if len(output) > max_string_length:\n        return output[:max_string_length] + '...'\n\n    if len(l) > max_elements_to_show:\n        return output + ', ...'\n\n    return output\n\n\ndef format_columns_for_condition(\n    columns: t.Union[Hashable, t.List[Hashable], None] = None,\n    ignore_columns: t.Union[Hashable, t.List[Hashable], None] = None\n) -> str:\n    \"\"\"Format columns properties for display in condition name.\n\n    Args:\n        columns (Union[Hashable, List[Hashable], None]):\n            columns to include into resulting string\n        ignore_columns (Union[Hashable, List[Hashable], None]):\n            columns to not include into resulting string\n\n    Returns: formatted string of columns\n    \"\"\"\n    if columns is not None:\n        columns = ensure_hashable_or_mutable_sequence(columns)\n        return f'columns: {\",\".join(map(str, columns))}'\n    elif ignore_columns is not None:\n        ignore_columns = ensure_hashable_or_mutable_sequence(ignore_columns)\n        return f'all columns ignoring: {\",\".join(map(str, ignore_columns))}'\n    else:\n        return 'all columns'\n", "path": "deepchecks/utils/strings.py"}], "after_files": [{"content": "# ----------------------------------------------------------------------------\n# Copyright (C) 2021 Deepchecks (https://www.deepchecks.com)\n#\n# This file is part of Deepchecks.\n# Deepchecks is distributed under the terms of the GNU Affero General\n# Public License (version 3 or later).\n# You should have received a copy of the GNU Affero General Public License\n# along with Deepchecks.  If not, see <http://www.gnu.org/licenses/>.\n# ----------------------------------------------------------------------------\n#\n\"\"\"String functions.\"\"\"\nimport random\nimport typing as t\nimport re\nfrom datetime import datetime\nfrom string import ascii_uppercase, digits\nfrom collections import defaultdict\nfrom decimal import Decimal\nfrom copy import copy\n\nimport pandas as pd\nfrom pandas.core.dtypes.common import is_numeric_dtype\n\nfrom deepchecks.utils.typing import Hashable\nfrom deepchecks.utils.validation import ensure_hashable_or_mutable_sequence\n\n\n__all__ = [\n    'string_baseform',\n    'get_base_form_to_variants_dict',\n    'split_camel_case',\n    'split_and_keep',\n    'split_by_order',\n    'is_string_column',\n    'format_percent',\n    'format_number',\n    'format_list',\n    'format_columns_for_condition',\n    'get_random_string',\n    'format_datetime'\n]\n\n\ndef get_random_string(n: int = 5):\n    \"\"\"Return random string at the given size.\n\n    Args:\n        n (int): the size of the string to return.\n    Returns:\n        (str): a random string\n    \"\"\"\n    return ''.join(random.choices(ascii_uppercase + digits, k=n))\n\n\ndef string_baseform(string: str) -> str:\n    \"\"\"Remove special characters from given string, leaving only a-z, A-Z, 0-9 characters.\n\n    Args:\n        string (str): string to remove special characters from\n\n    Returns:\n        (str): string without special characters\n    \"\"\"\n    if not isinstance(string, str):\n        return string\n    return re.sub('[^A-Za-z0-9]+', '', string).lower()\n\n\ndef is_string_column(column: pd.Series) -> bool:\n    \"\"\"Determine whether a pandas series is string type.\"\"\"\n    if is_numeric_dtype(column):\n        return False\n    try:\n        pd.to_numeric(column)\n        return False\n    except ValueError:\n        return True\n    # Non string objects like pd.Timestamp results in TypeError\n    except TypeError:\n        return False\n\n\ndef split_camel_case(string: str) -> str:\n    \"\"\"Split string where there are capital letters and enter space instead.\n\n    Args:\n        string (str): string to change\n    \"\"\"\n    return ' '.join(re.findall('[A-Z][^A-Z]*', string))\n\n\ndef get_base_form_to_variants_dict(uniques: t.Iterable[str]) -> t.Dict[str, t.Set[str]]:\n    \"\"\"Create dict of base-form of the uniques to their values.\n\n    function gets a set of strings, and returns a dictionary of shape Dict[str, Set]\n    the key being the \"base_form\" (a clean version of the string),\n    and the value being a set of all existing original values.\n    This is done using the StringCategory class.\n    \"\"\"\n    base_form_to_variants = defaultdict(set)\n    for item in uniques:\n        base_form_to_variants[string_baseform(item)].add(item)\n    return base_form_to_variants\n\n\ndef str_min_find(s: str, substr_list: t.Iterable[str]) -> t.Tuple[int, str]:\n    \"\"\"\n    Find the minimal first occurence of a substring in a string, and return both the index and substring.\n\n    Args:\n        s (str): The string in which we look for substrings\n        substr_list: list of substrings to find\n\n    Returns:\n        min_find (int): index of minimal first occurence of substring\n        min_substr (str): the substring that occures in said index\n\n    \"\"\"\n    min_find = -1\n    min_substr = ''\n    for substr in substr_list:\n        first_find = s.find(substr)\n        if first_find != -1 and (first_find < min_find or min_find == -1):\n            min_find = first_find\n            min_substr = substr\n    return min_find, min_substr\n\n\ndef split_and_keep(s: str, separators: t.Union[str, t.Iterable[str]]) -> t.List[str]:\n    \"\"\"\n    Split string by a another substring into a list. Like str.split(), but keeps the separator occurrences in the list.\n\n    Args:\n        s (str): the string to split\n        separators (str): the substring to split by\n\n    Returns:\n        List[str]: list of substrings, including the separator occurrences in string\n\n    \"\"\"\n    if isinstance(separators, str):\n        separators = [separators]\n\n    split_s = []\n    while len(s) != 0:\n        i, substr = str_min_find(s=s, substr_list=separators)\n        if i == 0:\n            split_s.append(substr)\n            s = s[len(substr):]\n        elif i == -1:\n            split_s.append(s)\n            break\n        else:\n            pre, _ = s.split(substr, 1)\n            split_s.append(pre)\n            s = s[len(pre):]\n    return split_s\n\n\ndef split_by_order(s: str, separators: t.Iterable[str], keep: bool = True) -> t.List[str]:\n    \"\"\"\n    Split string by a a list of substrings, each used once as a separator.\n\n    Args:\n        s (str): the string to split\n        separators (List[str]): list of substrings to split by\n        keep (bool): whether to keep the separators in list as well. Default is True.\n\n    Returns:\n        List[str]: list of substrings\n    \"\"\"\n    split_s = []\n    separators = list(copy(separators))\n    while len(s) != 0:\n        if len(separators) > 0:\n            sep = separators[0]\n            if s.find(sep) == 0:\n                if keep is True:\n                    split_s.append(sep)\n                s = s[len(sep):]\n                separators.pop(0)\n            else:\n                pre, _ = s.split(sep, 1)\n                split_s.append(pre)\n                s = s[len(pre):]\n        else:\n            split_s.append(s)\n            break\n    return split_s\n\n\ndef format_percent(ratio: float, floating_point: int = 2) -> str:\n    \"\"\"Format percent for elegant display.\n\n    Args:\n        ratio (float): Ratio to be displayed as percent\n        floating_point (int): Number of floating points to display\n\n    Returns:\n        String of ratio as percent\n    \"\"\"\n    result: str\n    if ratio < 0:\n        ratio = -ratio\n        prefix = '-'\n    else:\n        prefix = ''\n\n    if int(ratio) == ratio:\n        result = f'{int(ratio) * 100}%'\n    elif ratio > 1:\n        result = f'{ratio * 100:.{floating_point}f}%'\n    elif ratio < 10**(-(2+floating_point)):\n        result = f'{Decimal(ratio * 100):.{floating_point}E}%'\n    elif ratio > (1-10**(-(2+floating_point))):\n        if floating_point > 0:\n            result = f'99.{\"\".join([\"9\"]*floating_point)}%'\n        else:\n            result = '99%'\n    else:\n        result = f'{ratio:.{floating_point}%}'\n\n    return prefix + result\n\n\ndef format_number(x, floating_point: int = 2) -> str:\n    \"\"\"Format number for elegant display.\n\n    Args:\n        x (): Number to be displayed\n        floating_point (int): Number of floating points to display\n\n    Returns:\n        String of beautified number\n    \"\"\"\n    def add_commas(x):\n        return f'{x:,}'  # yes this actually formats the number 1000 to \"1,000\"\n\n    # 0 is lost in the next if case, so we have it here as a special use-case\n    if x == 0:\n        return '0'\n\n    # If x is a very small number, that would be rounded to 0, we would prefer to return it as the format 1.0E-3.\n    if abs(x) < 10 ** (-floating_point):\n        return f'{Decimal(x):.{floating_point}E}'\n\n    # If x is an integer, or if x when rounded is an integer (e.g. 1.999999), then return as integer:\n    if round(x) == round(x, floating_point):\n        return add_commas(round(x))\n\n    # If not, return as a float, but don't print unnecessary zeros at end:\n    else:\n        ret_x = round(x, floating_point)\n        return add_commas(ret_x).rstrip('0')\n\n\ndef format_list(l: t.List[Hashable], max_elements_to_show: int = 10, max_string_length: int = 40) -> str:\n    \"\"\"Format columns properties for display in condition name.\n\n    Args:\n        l (List): list to print.\n        max_elements_to_show (int): max elemnts to print before terminating.\n        max_string_length (int): max string length before terminating.\n\n    Return:\n        String of beautified list\n    \"\"\"\n    string_list = [str(i) for i in l[:max_elements_to_show]]\n    output = ', '.join(string_list)\n\n    if len(output) > max_string_length:\n        return output[:max_string_length] + '...'\n\n    if len(l) > max_elements_to_show:\n        return output + ', ...'\n\n    return output\n\n\ndef format_columns_for_condition(\n    columns: t.Union[Hashable, t.List[Hashable], None] = None,\n    ignore_columns: t.Union[Hashable, t.List[Hashable], None] = None\n) -> str:\n    \"\"\"Format columns properties for display in condition name.\n\n    Args:\n        columns (Union[Hashable, List[Hashable], None]):\n            columns to include into resulting string\n        ignore_columns (Union[Hashable, List[Hashable], None]):\n            columns to not include into resulting string\n\n    Returns: formatted string of columns\n    \"\"\"\n    if columns is not None:\n        columns = ensure_hashable_or_mutable_sequence(columns)\n        return f'columns: {\",\".join(map(str, columns))}'\n    elif ignore_columns is not None:\n        ignore_columns = ensure_hashable_or_mutable_sequence(ignore_columns)\n        return f'all columns ignoring: {\",\".join(map(str, ignore_columns))}'\n    else:\n        return 'all columns'\n\n\ndef format_datetime(\n    value,\n    datetime_format: str = '%Y/%m/%d %H:%M:%S.%f %Z%z'  # # 1992/02/13 13:23:00 UTC+0000\n) -> str:\n    \"\"\"Format datetime object or timestamp value.\n\n    Args:\n        value (Union[datetime, int, float]): datetime (timestamp) to format\n        format (str): format to use\n\n    Returns:\n        str: string representation of the provided value\n\n    Raises:\n        ValueError: if unexpected value type was passed to the function\n    \"\"\"\n    if isinstance(value, datetime):\n        return value.strftime(datetime_format)\n    elif isinstance(value, (int, float)):\n        return datetime.fromtimestamp(value).strftime(datetime_format)\n    else:\n        raise ValueError(f'Unsupported value type - {type(value).__name__}')\n", "path": "deepchecks/utils/strings.py"}]}