

# Importación de archivos y bibliotecas de Python a Athena para Spark
<a name="notebooks-import-files-libraries"></a>

En este documento, se proporcionan ejemplos de cómo importar archivos y bibliotecas de Python a Amazon Athena para Apache Spark.

## Condiciones y limitaciones
<a name="notebooks-import-files-libraries-considerations-limitations"></a>
+ **Versión de Python**: actualmente, Athena para Spark usa la versión 3.9.16 de Python. Tenga en cuenta que los paquetes de Python son sensibles a las versiones inferiores de Python.
+ **Athena para la arquitectura Spark**: Athena para Spark utiliza Amazon Linux 2 en la arquitectura ARM64. Tenga en cuenta que algunas bibliotecas de Python no distribuyen binarios para esta arquitectura.
+ **Objetos binarios compartidos (SO)**: dado que el método [addPyFile](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.SparkContext.addPyFile.html) de SparkContext no detecta objetos binarios compartidos, no se puede usar en Athena para que Spark agregue paquetes de Python que dependen de objetos compartidos.
+ **Conjuntos de datos distribuidos resilientes (RDD)**: no se admiten los [RDD](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.RDD.html).
+ **Dataframe.foreach**: no se admite el método [DataFrame.foreach](https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.foreach.html) de PySpark.

## Ejemplos
<a name="notebooks-import-files-libraries-examples"></a>

En los ejemplos, se utilizan las siguientes convenciones.
+ La ubicación de los marcadores de posición de Amazon S3 `s3://amzn-s3-demo-bucket`. Reemplácela por la ubicación de un bucket de S3 propio.
+ Todos los bloques de código que se ejecutan desde un intérprete de comandos de Unix se muestran como {{directory\_name}} `$`. Por ejemplo, el comando `ls` del directorio `/tmp` y su resultado se muestran de la siguiente manera:

  ```
  /tmp $ ls
  ```

  **Output**

  ```
  file1 file2
  ```

## Importación de archivos de texto para usarlos en cálculos
<a name="notebooks-import-files-libraries-importing-text-files"></a>

En los ejemplos de esta sección, se muestra cómo importar archivos de texto para usarlos en los cálculos de sus cuadernos de Athena para Spark.

### Adición de un archivo a un cuaderno después de escribirlo en el directorio temporal local
<a name="notebooks-import-files-libraries-adding-a-file-to-a-notebook-temporary-directory"></a>

En el siguiente ejemplo, se muestra cómo escribir un archivo en un directorio temporal local, agregarlo a un cuaderno y probarlo.

```
import os
from pyspark import SparkFiles
tempdir = '/tmp/'
path = os.path.join(tempdir, "test.txt")
with open(path, "w") as testFile:
    _ = testFile.write("5")
sc.addFile(path)

def func(iterator):
    with open(SparkFiles.get("test.txt")) as testFile:
        fileVal = int(testFile.readline())
        return [x * fileVal for x in iterator]

#Test the file
from pyspark.sql.functions import udf
from pyspark.sql.functions import col

udf_with_import = udf(func)
df = spark.createDataFrame([(1, "a"), (2, "b")])
df.withColumn("col", udf_with_import(col('_2'))).show()
```

**Output**

```
Calculation completed.
+---+---+-------+
| _1| _2|    col|
+---+---+-------+
|  1|  a|[aaaaa]|
|  2|  b|[bbbbb]|
+---+---+-------+
```

### Importar un archivo desde Amazon S3
<a name="notebooks-import-files-libraries-importing-a-file-from-s3"></a>

En el siguiente ejemplo, se muestra cómo importar un archivo de Amazon S3 a un cuaderno y cómo probarlo.

**Para importar un archivo de Amazon S3 a un cuaderno**

1. Cree un archivo llamado `test.txt` que tenga una sola línea con el valor `5`.

1. Agregue el archivo a un bucket de Amazon S3. En este ejemplo, se utiliza la ubicación `s3://amzn-s3-demo-bucket`.

1. Utilice el siguiente código para importar el archivo a su cuaderno y probarlo.

   ```
   from pyspark import SparkFiles
   sc.addFile('s3://amzn-s3-demo-bucket/test.txt')
   
   def func(iterator):
      with open(SparkFiles.get("test.txt")) as testFile:
          fileVal = int(testFile.readline())
          return [x * fileVal for x in iterator]
          
   #Test the file
   from pyspark.sql.functions import udf
   from pyspark.sql.functions import col
   
   udf_with_import = udf(func)
   df = spark.createDataFrame([(1, "a"), (2, "b")])
   df.withColumn("col", udf_with_import(col('_2'))).show()
   ```

   **Output**

   ```
   Calculation completed.
   +---+---+-------+
   | _1| _2|    col|
   +---+---+-------+
   |  1|  a|[aaaaa]|
   |  2|  b|[bbbbb]|
   +---+---+-------+
   ```

## Adición de archivos de Python
<a name="notebooks-import-files-libraries-adding-python-files"></a>

En los ejemplos de esta sección, se muestra cómo agregar archivos y bibliotecas de Python a sus cuadernos de Spark en Athena.

### Adición de archivos de Python y registro de una UDF
<a name="notebooks-import-files-libraries-adding-python-files-and-registering-a-udf"></a>

En el siguiente ejemplo, se muestra cómo agregar archivos de Python desde Amazon S3 a su cuaderno y cómo registrar una UDF.

**Para agregar archivos de Python a su cuaderno y registrar una UDF**

1. Con una ubicación propia de Amazon S3, cree el archivo `s3://amzn-s3-demo-bucket/file1.py` con el siguiente contenido:

   ```
   def xyz(input):
       return 'xyz  - udf ' + str(input);
   ```

1. En la misma ubicación de S3, cree el archivo `s3://amzn-s3-demo-bucket/file2.py` con el siguiente contenido:

   ```
   from file1 import xyz
   def uvw(input):
       return 'uvw -> ' + xyz(input);
   ```

1. En su cuaderno de Athena para Spark, ejecute los siguientes comandos.

   ```
   sc.addPyFile('s3://amzn-s3-demo-bucket/file1.py')
   sc.addPyFile('s3://amzn-s3-demo-bucket/file2.py')
   
   def func(iterator):
       from file2 import uvw
       return [uvw(x) for x in iterator]
   
   from pyspark.sql.functions import udf
   from pyspark.sql.functions import col
   
   udf_with_import = udf(func)
   
   df = spark.createDataFrame([(1, "a"), (2, "b")])
   
   df.withColumn("col", udf_with_import(col('_2'))).show(10)
   ```

   **Output**

   ```
   Calculation started (calculation_id=1ec09e01-3dec-a096-00ea-57289cdb8ce7) in (session=c8c09e00-6f20-41e5-98bd-4024913d6cee). Checking calculation status...
   Calculation completed.
   +---+---+--------------------+
   | _1| _2|                 col|
   +---+---+--------------------+
   | 1 |  a|[uvw -> xyz - ud... |
   | 2 |  b|[uvw -> xyz - ud... |
   +---+---+--------------------+
   ```

### Importación de un archivo .zip de Python
<a name="notebooks-import-files-libraries-importing-a-python-zip-file"></a>

Puede utilizar los métodos `addPyFile` y `import` de Python para importar un archivo .zip de Python a su cuaderno.

**nota**  
Los archivos `.zip` que importe a Athena para Spark pueden incluir solo paquetes de Python. Por ejemplo, no se admite la inclusión de paquetes con archivos basados en C.

**Para importar un archivo `.zip` de Python a su cuaderno**

1. En su equipo local, en un directorio de escritorio, por ejemplo `\tmp`, cree un directorio llamado `moduletest`.

1. En el directorio `moduletest`, cree un archivo denominado `hello.py` con el contenido siguiente:

   ```
   def hi(input):
       return 'hi ' + str(input);
   ```

1. En el mismo directorio, agregue un archivo vacío con el nombre `__init__.py`.

   Si saca un listado del contenido del directorio, ahora debería tener el siguiente aspecto.

   ```
   /tmp $ ls moduletest
   __init__.py       hello.py
   ```

1. Utilice el comando `zip` para agregar los dos archivos del módulo en un archivo llamado `moduletest.zip`.

   ```
   moduletest $ zip -r9 ../moduletest.zip *
   ```

1. Cargue el archivo `.zip` en el bucket de Amazon S3.

1. Use el siguiente código para importar el archivo `.zip` de Python a su cuaderno.

   ```
   sc.addPyFile('s3://amzn-s3-demo-bucket/moduletest.zip')
   
   from moduletest.hello import hi
   
   from pyspark.sql.functions import udf
   from pyspark.sql.functions import col
   
   hi_udf = udf(hi)
   
   df = spark.createDataFrame([(1, "a"), (2, "b")])
   
   df.withColumn("col", hi_udf(col('_2'))).show()
   ```

   **Output**

   ```
   Calculation started (calculation_id=6ec09e8c-6fe0-4547-5f1b-6b01adb2242c) in (session=dcc09e8c-3f80-9cdc-bfc5-7effa1686b76). Checking calculation status...
   Calculation completed.
   +---+---+----+
   | _1| _2| col|
   +---+---+----+
   |  1|  a|hi a|
   |  2|  b|hi b|
   +---+---+----+
   ```

### Importación de dos versiones de una biblioteca de Python como módulos independientes
<a name="notebooks-import-files-libraries-importing-two-library-versions"></a>

En los siguientes ejemplos de código, se muestra cómo agregar e importar dos versiones diferentes de una biblioteca de Python desde una ubicación de Amazon S3 como dos módulos independientes. El código agrega cada uno de los archivos de la biblioteca de S3, los importa y, a continuación, imprime la versión de la biblioteca para verificar la importación.

```
sc.addPyFile('s3://amzn-s3-demo-bucket/python-third-party-libs-test/simplejson_v3_15.zip')
sc.addPyFile('s3://amzn-s3-demo-bucket/python-third-party-libs-test/simplejson_v3_17_6.zip')

import simplejson_v3_15
print(simplejson_v3_15.__version__)
```

**Output**

```
3.15.0
```

```
import simplejson_v3_17_6
print(simplejson_v3_17_6.__version__)
```

**Output**

```
3.17.6
```

### Importación de un archivo .zip de Python desde PyPI
<a name="notebooks-import-files-libraries-importing-a-python-zip-file-from-a-github-project"></a>

En este ejemplo, se utiliza el comando `pip` para descargar un archivo .zip de Python del proyecto [bpabel/piglatin](https://github.com/bpabel/piglatin) desde el [Índice de paquetes de Python (PyPI)](https://pypi.org/).

**Para importar un archivo .zip de Python desde PyPI**

1. En el escritorio local, utilice los siguientes comandos para crear un directorio llamado `testpiglatin` y crear un entorno virtual.

   ```
   /tmp $ mkdir testpiglatin
   /tmp $ cd testpiglatin
   testpiglatin $ virtualenv .
   ```

   **Output**

   ```
   created virtual environment CPython3.9.6.final.0-64 in 410ms
   creator CPython3Posix(dest=/private/tmp/testpiglatin, clear=False, no_vcs_ignore=False, global=False)
   seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/user1/Library/Application Support/virtualenv)
   added seed packages: pip==22.0.4, setuptools==62.1.0, wheel==0.37.1
   activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
   ```

1. Cree un subdirectorio llamado `unpacked` para albergar el proyecto.

   ```
   testpiglatin $ mkdir unpacked
   ```

1. Utilice el comando `pip` para instalar el proyecto en el directorio `unpacked`.

   ```
   testpiglatin $ bin/pip install -t $PWD/unpacked piglatin
   ```

   **Output**

   ```
   Collecting piglatin
   Using cached piglatin-1.0.6-py2.py3-none-any.whl (3.1 kB)
   Installing collected packages: piglatin
   Successfully installed piglatin-1.0.6
   ```

1. Compruebe el contenido del directorio.

   ```
   testpiglatin $ ls
   ```

   **Output**

   ```
   bin lib pyvenv.cfg unpacked
   ```

1. Cambie al directorio `unpacked` y muestre el contenido.

   ```
   testpiglatin $ cd unpacked
   unpacked $ ls
   ```

   **Output**

   ```
   piglatin piglatin-1.0.6.dist-info
   ```

1. Utilice el comando `zip` para insertar el contenido del proyecto piglatin en un archivo llamado `library.zip`.

   ```
   unpacked $ zip -r9 ../library.zip *
   ```

   **Output**

   ```
   adding: piglatin/ (stored 0%)
   adding: piglatin/__init__.py (deflated 56%)
   adding: piglatin/__pycache__/ (stored 0%)
   adding: piglatin/__pycache__/__init__.cpython-39.pyc (deflated 31%)
   adding: piglatin-1.0.6.dist-info/ (stored 0%)
   adding: piglatin-1.0.6.dist-info/RECORD (deflated 39%)
   adding: piglatin-1.0.6.dist-info/LICENSE (deflated 41%)
   adding: piglatin-1.0.6.dist-info/WHEEL (deflated 15%)
   adding: piglatin-1.0.6.dist-info/REQUESTED (stored 0%)
   adding: piglatin-1.0.6.dist-info/INSTALLER (stored 0%)
   adding: piglatin-1.0.6.dist-info/METADATA (deflated 48%)
   ```

1. (Opcional) Utilice los siguientes comandos para probar la importación localmente.

   1. Establezca la ruta de Python en la ubicación del archivo `library.zip` e inicie Python.

      ```
      /home $ PYTHONPATH=/tmp/testpiglatin/library.zip 
      /home $ python3
      ```

      **Output**

      ```
      Python 3.9.6 (default, Jun 29 2021, 06:20:32)
      [Clang 12.0.0 (clang-1200.0.32.29)] on darwin
      Type "help", "copyright", "credits" or "license" for more information.
      ```

   1. Importe la biblioteca y ejecute un comando de prueba.

      ```
      >>> import piglatin
      >>> piglatin.translate('hello')
      ```

      **Output**

      ```
      'ello-hay'
      ```

1. Utilice comandos como los siguientes para agregar el archivo `.zip` desde Amazon S3, importarlo a su cuaderno de Athena y probarlo.

   ```
   sc.addPyFile('s3://amzn-s3-demo-bucket/library.zip')
   
   import piglatin
   piglatin.translate('hello')
   
   from pyspark.sql.functions import udf
   from pyspark.sql.functions import col
   
   hi_udf = udf(piglatin.translate)
   
   df = spark.createDataFrame([(1, "hello"), (2, "world")])
   
   df.withColumn("col", hi_udf(col('_2'))).show()
   ```

   **Output**

   ```
   Calculation started (calculation_id=e2c0a06e-f45d-d96d-9b8c-ff6a58b2a525) in (session=82c0a06d-d60e-8c66-5d12-23bcd55a6457). Checking calculation status...
   Calculation completed.
   +---+-----+--------+
   | _1|   _2|     col|
   +---+-----+--------+
   |  1|hello|ello-hay|
   |  2|world|orld-way|
   +---+-----+--------+
   ```

### Importación de un archivo .zip de Python desde PyPI con dependencias
<a name="notebooks-import-files-libraries-importing-a-python-zip-file-with-dependencies"></a>

En este ejemplo, se importa desde PyPI el paquete [https://github.com/makeworld-the-better-one/md2gemini](https://github.com/makeworld-the-better-one/md2gemini), que convierte el texto en Markdown al formato de texto Gemini. El paquete tiene las siguientes [dependencias](https://libraries.io/pypi/md2gemini):

```
cjkwrap
mistune
wcwidth
```

**Para importar un archivo .zip de Python con dependencias**

1. En su equipo local, utilice los siguientes comandos para crear un directorio llamado `testmd2gemini` y crear un entorno virtual.

   ```
   /tmp $ mkdir testmd2gemini
   /tmp $ cd testmd2gemini
   testmd2gemini$ virtualenv .
   ```

1. Cree un subdirectorio llamado `unpacked` para albergar el proyecto.

   ```
   testmd2gemini $ mkdir unpacked
   ```

1. Utilice el comando `pip` para instalar el proyecto en el directorio `unpacked`.

   ```
   /testmd2gemini $ bin/pip install -t $PWD/unpacked md2gemini
   ```

   **Output**

   ```
   Collecting md2gemini
     Downloading md2gemini-1.9.0-py3-none-any.whl (31 kB)
   Collecting wcwidth
     Downloading wcwidth-0.2.5-py2.py3-none-any.whl (30 kB)
   Collecting mistune<3,>=2.0.0
     Downloading mistune-2.0.2-py2.py3-none-any.whl (24 kB)
   Collecting cjkwrap
     Downloading CJKwrap-2.2-py2.py3-none-any.whl (4.3 kB)
   Installing collected packages: wcwidth, mistune, cjkwrap, md2gemini
   Successfully installed cjkwrap-2.2 md2gemini-1.9.0 mistune-2.0.2 wcwidth-0.2.5
   ...
   ```

1. Cambie al directorio `unpacked` y compruebe el contenido.

   ```
   testmd2gemini $ cd unpacked
   unpacked $ ls -lah
   ```

   **Output**

   ```
   total 16
   drwxr-xr-x  13 user1  wheel   416B Jun  7 18:43 .
   drwxr-xr-x   8 user1  wheel   256B Jun  7 18:44 ..
   drwxr-xr-x   9 user1  staff   288B Jun  7 18:43 CJKwrap-2.2.dist-info
   drwxr-xr-x   3 user1  staff    96B Jun  7 18:43 __pycache__
   drwxr-xr-x   3 user1  staff    96B Jun  7 18:43 bin
   -rw-r--r--   1 user1  staff   5.0K Jun  7 18:43 cjkwrap.py
   drwxr-xr-x   7 user1  staff   224B Jun  7 18:43 md2gemini
   drwxr-xr-x  10 user1  staff   320B Jun  7 18:43 md2gemini-1.9.0.dist-info
   drwxr-xr-x  12 user1  staff   384B Jun  7 18:43 mistune
   drwxr-xr-x   8 user1  staff   256B Jun  7 18:43 mistune-2.0.2.dist-info
   drwxr-xr-x  16 user1  staff   512B Jun  7 18:43 tests
   drwxr-xr-x  10 user1  staff   320B Jun  7 18:43 wcwidth
   drwxr-xr-x   9 user1  staff   288B Jun  7 18:43 wcwidth-0.2.5.dist-info
   ```

1. Utilice el comando `zip` para insertar el contenido del proyecto md2gemini en un archivo llamado `md2gemini.zip`.

   ```
   unpacked $ zip -r9 ../md2gemini *
   ```

   **Output**

   ```
     adding: CJKwrap-2.2.dist-info/ (stored 0%)
     adding: CJKwrap-2.2.dist-info/RECORD (deflated 37%)
     ....
     adding: wcwidth-0.2.5.dist-info/INSTALLER (stored 0%)
     adding: wcwidth-0.2.5.dist-info/METADATA (deflated 62%)
   ```

1. (Opcional) Utilice los siguientes comandos para comprobar que la biblioteca funciona en su equipo local.

   1. Establezca la ruta de Python en la ubicación del archivo `md2gemini.zip` e inicie Python.

      ```
      /home $ PYTHONPATH=/tmp/testmd2gemini/md2gemini.zip 
      /home python3
      ```

   1. Importe la biblioteca y realice una prueba.

      ```
      >>> from md2gemini import md2gemini
      >>> print(md2gemini('[abc](https://abc.def)'))
      ```

      **Output**

      ```
      https://abc.def abc
      ```

1. Utilice los siguientes comandos para agregar el archivo `.zip` desde Amazon S3, importarlo a su cuaderno de Athena y realizar una prueba no UDF.

   ```
   # (non udf test)
   sc.addPyFile('s3://amzn-s3-demo-bucket/md2gemini.zip')
   from md2gemini import md2gemini
   print(md2gemini('[abc](https://abc.def)'))
   ```

   **Output**

   ```
   Calculation started (calculation_id=0ac0a082-6c3f-5a8f-eb6e-f8e9a5f9bc44) in (session=36c0a082-5338-3755-9f41-0cc954c55b35). Checking calculation status...
   Calculation completed.
   => https://abc.def (https://abc.def/) abc
   ```

1. Utilice los siguientes comandos para realizar una prueba UDF.

   ```
   # (udf test)
   
   from pyspark.sql.functions import udf
   from pyspark.sql.functions import col
   from md2gemini import md2gemini
   
   
   hi_udf = udf(md2gemini)
   df = spark.createDataFrame([(1, "[first website](https://abc.def)"), (2, "[second website](https://aws.com)")])
   df.withColumn("col", hi_udf(col('_2'))).show()
   ```

   **Output**

   ```
   Calculation started (calculation_id=60c0a082-f04d-41c1-a10d-d5d365ef5157) in (session=36c0a082-5338-3755-9f41-0cc954c55b35). Checking calculation status...
   Calculation completed.
   +---+--------------------+--------------------+
   | _1|                  _2|                 col|
   +---+--------------------+--------------------+
   |  1|[first website](h...|=> https://abc.de...|
   |  2|[second website](...|=> https://aws.co...|
   +---+--------------------+--------------------+
   ```