diff --git a/src/snowflake/snowpark/dataframe.py b/src/snowflake/snowpark/dataframe.py index 10844338a8..4afde3b5f1 100644 --- a/src/snowflake/snowpark/dataframe.py +++ b/src/snowflake/snowpark/dataframe.py @@ -814,6 +814,22 @@ def collect( See also: :meth:`collect_nowait()` + + The default blocking call retrieves all result rows into client memory. + For large results, consider :meth:`to_local_iterator` instead of + materializing a list. Use :meth:`sort` before collecting when the order + of rows matters. + + Example:: + + >>> df = session.create_dataframe([(2, "Bob"), (1, "Alice")], schema=["id", "name"]) + >>> rows = df.sort("id").collect() + >>> rows + [Row(ID=1, NAME='Alice'), Row(ID=2, NAME='Bob')] + >>> rows[0]["NAME"] + 'Alice' + >>> rows[1].ID + 2 """ kwargs = {} @@ -6905,6 +6921,25 @@ def queries(self) -> Dict[str, List[str]]: Returns a ``dict`` that contains a list of queries that will be executed to evaluate this DataFrame with the key `queries`, and a list of post-execution actions (e.g., queries to clean up temporary objects) with the key `post_actions`. + + ``queries`` contains SQL statements in the DataFrame's execution plan; + a plan can contain more than one statement. ``post_actions`` contains + cleanup statements associated with that plan and can be empty. These + lists aren't result rows or a history of previously executed queries. + + Use :meth:`explain` for a printed query list and, when available, a + database execution plan. Use :meth:`Session.query_history` to record + queries executed through a session. Generated SQL can change between + library versions and configurations; don't rely on exact SQL text. + + Example:: + + >>> df = session.range(3) + >>> planned_sql = df.queries + >>> sorted(planned_sql) + ['post_actions', 'queries'] + >>> isinstance(planned_sql["queries"], list) + True """ plan_queries = self._plan.execution_queries return { diff --git a/src/snowflake/snowpark/dataframe_stat_functions.py b/src/snowflake/snowpark/dataframe_stat_functions.py index 5d768c6078..306025f52d 100644 --- a/src/snowflake/snowpark/dataframe_stat_functions.py +++ b/src/snowflake/snowpark/dataframe_stat_functions.py @@ -426,6 +426,12 @@ def sample_by( ) -> "snowflake.snowpark.DataFrame": """Returns a DataFrame containing a stratified sample without replacement, based on a ``dict`` that specifies the fraction for each stratum. + ``df.stat`` is the :class:`DataFrameStatFunctions` accessor for ``df``; + it groups statistical operations and doesn't select a column named + ``stat``. ``df.stat.sample_by(...)``, ``df.sample_by(...)``, and + ``df.sampleBy(...)`` call the same sampling operation. Separate calls + can return different random samples. + Example:: >>> df = session.create_dataframe([("Bob", 17), ("Alice", 10), ("Nico", 8), ("Bob", 12)], schema=["name", "age"]) diff --git a/src/snowflake/snowpark/dataframe_writer.py b/src/snowflake/snowpark/dataframe_writer.py index 180a9c5a59..9f013194b9 100644 --- a/src/snowflake/snowpark/dataframe_writer.py +++ b/src/snowflake/snowpark/dataframe_writer.py @@ -275,8 +275,9 @@ def save_as_table( column_order: When ``mode`` is "append", data will be inserted into the target table by matching column sequence or column name. Default is "index". When ``mode`` is not "append", the ``column_order`` makes no difference. - "index": Data will be inserted into the target table by column sequence. - "name": Data will be inserted into the target table by matching column names. If the target table has more columns than the source DataFrame, use this one. + - ``"index"``: Data will be inserted into the target table by column sequence. + - ``"name"``: Data will be inserted into the target table by matching column names. + If the target table has more columns than the source DataFrame, use this one. create_temp_table: (Deprecated) The to-be-created table will be temporary if this is set to ``True``. table_type: The table type of table to be created. The supported values are: ``temp``, ``temporary``, diff --git a/src/snowflake/snowpark/file_operation.py b/src/snowflake/snowpark/file_operation.py index 0abe534046..2a0f4fbefc 100644 --- a/src/snowflake/snowpark/file_operation.py +++ b/src/snowflake/snowpark/file_operation.py @@ -24,7 +24,29 @@ class PutResult(NamedTuple): - """Represents the results of uploading a local file to a stage location.""" + """Represents the results of uploading a local file to a stage location. + + :meth:`FileOperation.put` returns a list of these objects. Inspect each + object's ``source``, ``target``, ``status``, and ``message`` attributes to + identify the file and its upload outcome. ``SKIPPED`` means the file wasn't + uploaded; it doesn't mean a new upload succeeded. + + This example requires an existing internal stage named ``example_stage`` + and a session authorized to upload to it. It creates only a local text + fixture; use a stage intended for testing. File names and sizes in the + returned objects depend on the input and compression options. + + Example:: + + >>> from pathlib import Path + >>> from tempfile import TemporaryDirectory + >>> with TemporaryDirectory() as directory: # doctest: +SKIP + ... path = Path(directory) / "message.txt" + ... _ = path.write_text("hello", encoding="utf-8") + ... results = session.file.put(str(path), "@example_stage", auto_compress=False) + ... for result in results: + ... print(result.source, result.target, result.status, result.message) + """ source: str #: The source file path. target: str #: The file path in the stage where the source file is uploaded. diff --git a/src/snowflake/snowpark/functions.py b/src/snowflake/snowpark/functions.py index f55eba8c1d..9f35db90f9 100644 --- a/src/snowflake/snowpark/functions.py +++ b/src/snowflake/snowpark/functions.py @@ -7005,6 +7005,16 @@ def array_agg( """Returns the input values, pivoted into an ARRAY. If the input is empty, an empty ARRAY is returned. + Element order is unpredictable unless you specify :meth:`Column.within_group` + on the aggregate expression. Sorting the result DataFrame orders result + rows, not the elements inside each array. This also applies to the + ``collect_list`` alias. When ``is_distinct=True``, the ordering expression + must refer to the same column as the aggregate input. + + For example, this expression orders elements within each array by ``a``:: + + >>> ordered_values = array_agg("a").within_group("a") + Example:: >>> df = session.create_dataframe([[1], [2], [3], [1]], schema=["a"]) >>> df.select(array_agg("a", True).within_group("a").alias("result")).show() @@ -7018,6 +7028,18 @@ def array_agg( |] | ------------ + + ``collect_list`` is an alias of ``array_agg`` with the same arguments and + behavior. The two names share this documentation, which is why the example + above uses ``array_agg``. By default, duplicates are retained. + + This example produces an array containing ``[1, 2, 2]``: + + Example using the alias:: + + >>> from snowflake.snowpark.functions import collect_list + >>> df = session.create_dataframe([[2], [1], [2]], schema=["a"]) + >>> result = df.select(collect_list("a").within_group("a").alias("values")).collect() """ ast = build_function_expr("array_agg", [col, is_distinct]) if _emit_ast else None c = _to_col_if_str(col, "array_agg") diff --git a/src/snowflake/snowpark/session.py b/src/snowflake/snowpark/session.py index a6448b59af..bb83a230d8 100644 --- a/src/snowflake/snowpark/session.py +++ b/src/snowflake/snowpark/session.py @@ -3269,6 +3269,24 @@ def get_session_stage( These artifacts include libraries and packages for UDFs that you define in this session via :func:`add_import`. + The return value is a Snowflake stage reference beginning with ``@``, + not a local directory or the contents of a file. Pass it to file + operations such as :meth:`FileOperation.put` to upload artifacts to + Snowflake. Treat this session-scoped temporary storage as disposable, + not as a permanent location for application data. + + Example:: + + >>> stage = session.get_session_stage() # doctest: +SKIP + >>> stage.startswith("@") # doctest: +SKIP + True + >>> session.get_session_stage() == stage # doctest: +SKIP + True + + The stage name is generated by Snowpark, so don't hardcode the name + returned by another session. The first call can create a temporary + stage in Snowflake and requires a connection with appropriate privileges. + Note: This temporary stage is created once under the current database and schema of a Snowpark session. Therefore, if you switch database or schema during the session, the stage will not be re-created @@ -4302,6 +4320,19 @@ def get_current_account(self) -> Optional[str]: """ Returns the name of the current account for the Python connector session attached to this session. + + The value comes from the attached connection; it isn't a database or + schema name. The returned string depends on the connection's account + configuration. It can be ``None`` if that parameter isn't available. + + Example:: + + >>> account = session.get_current_account() + >>> isinstance(account, (str, type(None))) + True + + Use ``account`` when you need the account configured for an existing + session, rather than creating a new connection to inspect it. """ return self._conn._get_current_parameter("account") diff --git a/src/snowflake/snowpark/table.py b/src/snowflake/snowpark/table.py index 0dbe7199cf..d12d797be2 100644 --- a/src/snowflake/snowpark/table.py +++ b/src/snowflake/snowpark/table.py @@ -446,7 +446,9 @@ def sample( This is the main difference between :meth:`DataFrame.sample` and this method. Args: - frac: The percentage of rows to be sampled. + frac: The fraction of rows to sample, from 0.0 to 1.0 inclusive. + For example, ``0.25`` requests approximately 25 percent of rows, + not exactly one quarter of the table. Specify either ``frac`` or ``n``. n: The fixed number of rows to sample in the range of 0 to 1,000,000 (inclusive). Either ``frac`` or ``n`` should be provided. seed: Specifies a seed value to make the sampling deterministic. Can be any integer between 0 and 2147483647 inclusive. Default value is ``None``. @@ -461,6 +463,18 @@ def sample( - Fixed-size sampling can be slower than equivalent fraction-based sampling because fixed-size sampling prevents some query optimization. - Fixed-size sampling doesn't work with SYSTEM | BLOCK sampling. + These examples require an existing table named ``sample_source``. + Sampling doesn't guarantee row order, and fraction-based sampling + doesn't guarantee a particular row count. + + Examples:: + + >>> table = session.table("sample_source") # doctest: +SKIP + >>> fraction_sample = table.sample(frac=0.25, seed=7) # doctest: +SKIP + >>> fixed_sample = table.sample(n=5) # doctest: +SKIP + >>> len(fixed_sample.collect()) <= 5 # doctest: +SKIP + True + """ DataFrame._validate_sample_input(frac, n) if sampling_method and sampling_method.upper() not in (