Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions src/snowflake/snowpark/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -6200,14 +6200,33 @@ def sample(
n: Optional[int] = None,
_emit_ast: bool = True,
) -> "DataFrame":
"""Samples rows based on either the number of rows to be returned or a
percentage of rows to be returned.
"""Returns a random sample of rows using Snowflake's SQL SAMPLE clause.

Specify either ``frac`` or ``n``. Fractional sampling includes each row
with the given probability, so the number of returned rows can vary.
Fixed-size sampling returns the requested number of rows, or all rows
if the input contains fewer rows. Neither form guarantees row order.
Repeated executions can return different samples; this method has no
seed parameter. For seeded sampling of a table, see :meth:`Table.sample`.

See `SAMPLE <https://docs.snowflake.com/en/sql-reference/constructs/sample>`_
for SQL sampling semantics.

Args:
frac: the percentage of rows to be sampled.
frac: The probability of selecting each row, from 0.0 to 1.0
inclusive. For example, 0.1 requests approximately 10 percent
of the rows, not exactly 10 percent.
n: the number of rows to sample in the range of 0 to 1,000,000 (inclusive).

Returns:
a :class:`DataFrame` containing the sample of rows.

Examples::

>>> df = session.range(100)
>>> fractional_sample = df.sample(frac=0.1)
>>> fixed_sample = df.sample(n=5)
>>> assert fixed_sample.count() == 5
"""
DataFrame._validate_sample_input(frac, n)

Expand Down Expand Up @@ -6263,6 +6282,15 @@ def ai(self) -> DataFrameAIFunctions:
"""
Returns a :class:`DataFrameAIFunctions` object that provides AI-powered functions
for the DataFrame.

Access this namespace through an existing DataFrame, for example
``df.ai``. It is not a column and accessing it does not itself execute
an AI function. Call a method on the namespace to build an AI operation.

See :meth:`DataFrameAIFunctions.classify`,
:meth:`DataFrameAIFunctions.extract`, and
:meth:`DataFrameAIFunctions.sentiment` for parameters and examples.
The :class:`DataFrameAIFunctions` reference lists the available methods.
"""
return self._ai

Expand Down
20 changes: 20 additions & 0 deletions src/snowflake/snowpark/dataframe_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,26 @@ def load(self, path: Optional[str] = None, _emit_ast: bool = True) -> DataFrame:
def csv(self, path: str, _emit_ast: bool = True) -> DataFrame:
"""Specify the path of the CSV file(s) to load.

Configure the reader with :meth:`option`, :meth:`options`, and
:meth:`schema` before calling ``csv``. Options are not keyword
arguments of this method. Common CSV format options include
``FIELD_DELIMITER``, ``SKIP_HEADER``, and
``FIELD_OPTIONALLY_ENCLOSED_BY``. See the
`CSV format options
<https://docs.snowflake.com/en/sql-reference/sql/create-file-format#type-csv>`_
for their accepted values.

Provide a schema explicitly or enable the reader's ``INFER_SCHEMA``
option. Schema inference can issue queries while constructing the
DataFrame and is not supported for CSV in local testing mode.

Example (requires an existing stage containing a header row and two
comma-separated columns)::

>>> from snowflake.snowpark.types import StructType, StructField, IntegerType, StringType
>>> schema = StructType([StructField("id", IntegerType()), StructField("name", StringType())])
>>> df = session.read.schema(schema).option("SKIP_HEADER", 1).csv("@my_stage/people.csv") # doctest: +SKIP

Args:
path: The stage location of a CSV file, or a stage location that has CSV files.

Expand Down
4 changes: 3 additions & 1 deletion src/snowflake/snowpark/dataframe_stat_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ def approx_quantile(

Args:
col: The name of the numeric column.
percentile: A list of float values greater than or equal to 0.0 and less than 1.0.
percentile: A list of float values between 0.0 and 1.0, inclusive.
For example, 0.5 requests the approximate median and 1.0 requests
the approximate maximum.
statement_params: Dictionary of statement level parameters to be set while executing this action.

Returns:
Expand Down
17 changes: 17 additions & 0 deletions src/snowflake/snowpark/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ class SnowparkSQLException(SnowparkClientException):
Includes all error codes in range 13XX (where XX is 0-9).

This exception is specifically raised for error codes: 1300, 1304.

Attributes:
error_code: Snowpark client error code. This is distinct from the
underlying Snowflake SQL error code.
sql_error_code: Snowflake SQL error number, when provided by the connector.
sfqid: Snowflake query ID, when available. Use this to locate the failed
statement in `Query History
<https://docs.snowflake.com/en/user-guide/ui-query-history>`_.
query: SQL text associated with the error, when available.
raw_message: Underlying error message, when available.
conn_error: Original connector exception, when available.

Read the error message first: a compilation error, missing object, and
insufficient privilege require different fixes even if they share a
Snowpark error code. Check the failed query's database, schema, role, and
referenced identifiers. When requesting help, include the query ID and
error codes; redact sensitive literals from SQL and error messages.
"""

def __init__(
Expand Down
6 changes: 6 additions & 0 deletions src/snowflake/snowpark/file_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ def get(
The command lists all files in the specified path and applies the regular expression pattern on each of the files found.
Default: ``None`` (all files in the specified stage are downloaded).
statement_params: Dictionary of statement level parameters to be set while executing this action.
For example, ``{"QUERY_TAG": "download_files"}`` tags the GET
statement. See `Snowflake parameters
<https://docs.snowflake.com/en/sql-reference/parameters>`_ for
parameter meanings and allowed values. Only parameters supported
at statement level apply; this is not a way to set account-only
parameters or GET options such as ``parallel`` and ``pattern``.

Returns:
A ``list`` of :class:`GetResult` instances, each of which represents the result of a downloaded file.
Expand Down
14 changes: 13 additions & 1 deletion src/snowflake/snowpark/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,19 @@ def open(
require_scoped_url: bool = True,
) -> SnowflakeFile:
"""
Used to create a :class:`~snowflake.snowpark.file.SnowflakeFile` which can only be used for read-based IO operations on the file.
Opens a file for reading and returns a :class:`SnowflakeFile` stream.
Use a ``with`` statement to close the stream after reading.

For example, inside a handler with a caller-provided scoped URL::

from snowflake.snowpark.files import SnowflakeFile

def read_text(url):
with SnowflakeFile.open(url, "r") as source:
return source.read()

This example reads the whole file into memory. Use binary mode
(``"rb"``) when the consumer expects bytes rather than text.

In UDFs and Stored Procedures, the object works like a read-only Python IOBase object and as a wrapper for an IO stream of remote files.

Expand Down
40 changes: 25 additions & 15 deletions src/snowflake/snowpark/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7356,24 +7356,27 @@ def array_insert(
def array_position(
variant: ColumnOrName, array: ColumnOrName, _emit_ast: bool = True
) -> Column:
"""Returns the index of the first occurrence of an element in an ARRAY.
"""Returns the zero-based index of the first occurrence of an element in an ARRAY.

The first element has index 0. If the value is not present, returns SQL NULL
(represented by ``None`` in a collected Row), not -1.

Args:
variant: Column containing the VARIANT value that you want to find. The function
searches for the first occurrence of this value in the array.
array: Column containing the ARRAY to be searched.

Example::
>>> from snowflake.snowpark import Row
>>> df = session.create_dataframe([Row([2, 1]), Row([1, 3])], schema=["a"])
>>> df.select(array_position(lit(1), "a").alias("result")).show()
------------
|"RESULT" |
------------
|1 |
|0 |
------------
<BLANKLINE>
>>> from snowflake.snowpark.functions import array_position, lit
>>> df = session.create_dataframe(
... [(1, [2, 1, 1]), (2, [1, 3]), (3, [4, 5])],
... schema=["id", "values"])
>>> df.select("id", array_position(lit(1), "values").alias("position")).sort("id").collect()
[Row(ID=1, POSITION=1), Row(ID=2, POSITION=0), Row(ID=3, POSITION=None)]

In the first row, 1 appears twice; the result is the position of its first
occurrence. Use :func:`lit` to search for a literal value rather than a
column name.
"""
v = _to_col_if_str(variant, "array_position")
a = _to_col_if_str(array, "array_position")
Expand Down Expand Up @@ -8834,13 +8837,20 @@ def iff(
expr1: A :class:`Column` expression or a literal value, which will be returned
if ``condition`` is true.
expr2: A :class:`Column` expression or a literal value, which will be returned
if ``condition`` is false.
if ``condition`` is false or NULL.

Examples::

>>> df = session.create_dataframe([True, False, None], schema=["a"])
>>> df.select(iff(df["a"], lit("true"), lit("false")).alias("iff")).collect()
[Row(IFF='true'), Row(IFF='false'), Row(IFF='false')]
>>> from snowflake.snowpark.functions import iff, lit
>>> df = session.create_dataframe(
... [(1, True), (2, False), (3, None)], schema=["id", "approved"])
>>> df.select(
... "id", iff(df["approved"], lit("ship"), lit("hold")).alias("action")
... ).sort("id").collect()
[Row(ID=1, ACTION='ship'), Row(ID=2, ACTION='hold'), Row(ID=3, ACTION='hold')]

Only an approved row selects ``"ship"``. Both false and unknown (NULL)
approval select ``"hold"``.
"""
ast = build_function_expr("iff", [condition, expr1, expr2]) if _emit_ast else None
return _call_function(
Expand Down
35 changes: 35 additions & 0 deletions src/snowflake/snowpark/modin/plugin/extensions/pd_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,41 @@ def to_snowflake(
index_label: Optional[IndexLabel] = None,
table_type: Literal["", "temp", "temporary", "transient"] = "",
) -> None:
"""Save a Snowpark pandas DataFrame or Series as a Snowflake table.

Args:
obj: The Snowpark pandas DataFrame or Series to write.
name: Destination table name or fully-qualified identifier, such as
``"MY_DB.MY_SCHEMA.MY_TABLE"`` or
``["MY_DB", "MY_SCHEMA", "MY_TABLE"]``. Unqualified names use the
session's current database and schema. Double-quote identifier
components to preserve mixed case.
if_exists: How to handle an existing table. Defaults to ``"fail"``:

- ``"fail"``: Raise ValueError if the table exists.
- ``"replace"``: Drop the table and write the new values.
- ``"append"``: Add rows to the existing table.

index: If True (the default), save index levels as table columns in
addition to the data columns. Set False to omit the index.
index_label: Column label or labels for the saved index. Defaults to
the index names. Supply a label for an unnamed index and one label
per level for a MultiIndex. Labels must not duplicate data-column
labels. Ignored when ``index=False``.
table_type: Type of table to create: ``"temp"`` or ``"temporary"``,
``"transient"``, or ``""`` (the default) for a permanent table.
See `table types
<https://docs.snowflake.com/en/user-guide/tables-temp-transient>`_.

Returns:
None. The write is executed by this call. Row ordering is not preserved
by a Snowflake table.

See also:
:func:`DataFrame.to_snowflake <modin.pandas.DataFrame.to_snowflake>`,
:func:`Series.to_snowflake <modin.pandas.Series.to_snowflake>`,
:func:`read_snowflake <modin.pandas.read_snowflake>`.
"""
_snowpark_pandas_obj_check(obj)
return obj.to_snowflake(
name=name,
Expand Down
40 changes: 39 additions & 1 deletion src/snowflake/snowpark/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,23 @@ def conf(self) -> RuntimeConfig:
def sql_simplifier_enabled(self) -> bool:
"""Set to ``True`` to use the SQL simplifier (defaults to ``True``).
The generated SQLs from ``DataFrame`` transformations would have fewer layers of nested queries if the SQL simplifier is enabled.

Set this property before constructing the DataFrame whose SQL you want
to inspect. SQL text can change between library versions; compare
:attr:`DataFrame.queries` rather than relying on a specific SQL string.

Example::

>>> original_setting = session.sql_simplifier_enabled
>>> try:
... session.sql_simplifier_enabled = True
... df = session.range(10).select("id").filter("id > 2")
... simplified_queries = df.queries["queries"]
... session.sql_simplifier_enabled = False
... df = session.range(10).select("id").filter("id > 2")
... unsimplified_queries = df.queries["queries"]
... finally:
... session.sql_simplifier_enabled = original_setting
"""
return self._sql_simplifier_enabled

Expand Down Expand Up @@ -3156,6 +3173,19 @@ def sql(
or :func:`DataFrame.to_pandas` evaluate the DataFrame.
For **immediate execution**, chain the call with the collect method: `session.sql(query).collect()`.

SQL compilation and execution errors usually surface when an action
executes the query, not when this method creates the DataFrame.
Operations that request schema metadata can also contact Snowflake
before collection. Catch :class:`~snowflake.snowpark.exceptions.SnowparkSQLException`
around the operation that triggers evaluation; inspect its message
and query ID to diagnose the server error. This is not an exhaustive
list of possible client, connection, or argument errors.

Raises:
NotImplementedError: SQL execution is not supported in local testing
mode. See `mocking SQL operations
<https://docs.snowflake.com/en/developer-guide/snowpark/python/testing-locally>`_.

Args:
query: The SQL statement to execute.
params: binding parameters. We only support qmark bind variables. For more information, check
Expand Down Expand Up @@ -3520,7 +3550,11 @@ def write_pandas(

Args:
df: The pandas DataFrame or Snowpark pandas DataFrame or Series we'd like to write back.
table_name: Name of the table we want to insert into.
table_name: Name of the table we want to insert into, without the
database or schema prefix. Pass those separately through
``database`` and ``schema``. For example, use
``table_name="MY_TABLE", database="MY_DB", schema="MY_SCHEMA"``,
not ``table_name="MY_DB.MY_SCHEMA.MY_TABLE"``.
database: Database that the table is in. If not provided, the default one will be used.
schema: Schema that the table is in. If not provided, the default one will be used.
chunk_size: Number of rows to be inserted once. If not provided, all rows will be dumped once.
Expand All @@ -3537,6 +3571,10 @@ def write_pandas(
quote_identifiers: By default, identifiers, specifically database, schema, table and column names
(from :attr:`DataFrame.columns`) will be quoted. If set to ``False``, identifiers
are passed on to Snowflake without quoting, i.e. identifiers will be coerced to uppercase by Snowflake.
With the default ``True``, names must match the stored case:
an object created with an unquoted name normally has an uppercase
name, whereas a quoted mixed-case name must retain its case.
Do not uppercase names of quoted mixed-case objects.
auto_create_table: When true, automatically creates a table to store the passed in pandas DataFrame using the
passed in ``database``, ``schema``, and ``table_name``. Note: there are usually multiple table configurations that
would allow you to upload a particular pandas DataFrame successfully. If you don't like the auto created
Expand Down
Loading