Tüm Ürünler
Search
Belge Merkezi

MaxCompute:Use UDFs and the third-party Python libraries

Son Güncelleme:Oct 04, 2026

PyODPS DataFrame, yerleşik hesaplamaları kullanıcı tanımlı işlevler (UDF) ve üçüncü taraf Python paketleriyle genişletmenize olanak tanır. Bu konu; map ile öğe düzeyinde eşleme, apply ile satır düzeyi dönüşümler ve özel toplamlar, UDF'ler içindeki kaynak referansları ile üçüncü taraf paketlerin nasıl yükleneceği ve yapılandırılacağı konularını kapsar.

Ön koşullar

Başlamadan önce şunlara sahip olduğunuzdan emin olun:

  • MaxCompute tablosundan veya pandas DataFrame'den oluşturulmuş bir DataFrame nesnesi

  • MaxCompute projenizde etkinleştirilmiş Python UDF desteği (Python işlevleriyle map ve apply için gereklidir)

Alibaba Cloud genel hizmetleri Python UDF'yi desteklemez. Projeniz Python UDF'leri desteklemiyorsa map yöntemi ve buna bağlı yerleşik işlevler kullanılamaz.

Bilinen sınırlamalar

Sınırlama Ayrıntılar
Desteklenmeyen türler map ve apply yöntemleri giriş veya çıkış olarak LIST ya da DICT türlerini kabul etmez.
Önceden yüklenmiş ikili kitaplık C kodu içeren tek önceden yüklenmiş üçüncü taraf kitaplık NumPy'dir. Diğer tüm ikili kitaplıkların açıkça yüklenmesi gerekir.
Python 2/3 uyumluluğu Python sürümleri arasındaki bayt kodu farklılıkları nedeniyle, Python 3'e özel sözdizimi kullanan kodlar (örneğin yield from) Python 2.7 çalıştıran bir MaxCompute Worker'da başarısız olabilir. Kodunuzu Python 3'teki MapReduce API'sini kullanarak üretim ortamına dağıtmadan önce doğru çalıştığını doğrulayın.
İkili paketler için derleme platformu macOS veya Windows üzerinde oluşturulan Wheel dosyaları MaxCompute'da kullanılamaz. İkili paketleri bir Linux kabuğunda oluşturun.

Bir sütuna UDF uygulama

Her öğede bir UDF çağırmak için bir Sequence nesnesinde map yöntemini kullanın.

>>> iris.sepallength.map(lambda x: x + 1).head(5)
   sepallength
0          6.1
1          5.9
2          5.7
3          5.6
4          6.0

map işleminden sonra Sequence türü değişirse yeni türü açıkça belirtin:

>>> iris.sepallength.map(lambda x: 't' + str(x), 'string').head(5)
   sepallength
0         t5.1
1         t4.9
2         t4.7
3         t4.6
4         t5.0

Kapanış değişken yakalama hatalarından kaçının

Bir UDF kapanış içerdiğinde, yakalanan değişkende yapılan harici değişiklikler işlevin davranışını etkiler. Aşağıdaki kod istenmeyen bir sonuç üretir; dfs içindeki her SequenceExpr sonunda df.sepal_length + 9 olur:

>>> dfs = []
>>> for i in range(10):
>>>     dfs.append(df.sepal_length.map(lambda x: x + i))

Bu sorunu çözmek için lambda'yı dış bir işlevden döndürün veya functools.partial kullanın:

# Option 1: use a factory function
>>> dfs = []
>>> def get_mapper(i):
>>>     return lambda x: x + i
>>> for i in range(10):
>>>     dfs.append(df.sepal_length.map(get_mapper(i)))
# Option 2: use functools.partial
>>> import functools
>>> dfs = []
>>> for i in range(10):
>>>     dfs.append(df.sepal_length.map(functools.partial(lambda v, x: x + v, i)))

Mevcut UDF'leri kullanma

Mevcut bir UDF'yi çağırmak için map öğesine bir işlev adı (dize) veya Function nesnesi iletin. Ayrıntılar için bkz. İşlevler.

Sayaçlarla yürütmeyi izleme

Bir UDF içinden sayaçlara erişmek için get_execution_context kullanın. Sayaç değerleri LogView'un JSONSummary bölümünde görünür.

from odps.udf import get_execution_context

def h(x):
    ctx = get_execution_context()
    counters = ctx.get_counters()
    counters.get_counter('df', 'add_one').increment(1)
    return x + 1

df.field.map(h)

Bir satıra UDF uygulama

Her satırda bir UDF çağırmak için axis=1 ile apply kullanın. UDF bir seferde bir satır alır; alan değerlerini nitelik adına veya dizine göre alın.

Satır başına tek değer döndürme

Bir Sequence döndürmek için reduce=True ayarlayın. Çıkış türünü types parametresiyle belirtin (varsayılan STRING'dir).

>>> iris.apply(lambda row: row.sepallength + row.sepalwidth, axis=1, reduce=True, types='float').rename('sepaladd').head(3)
   sepaladd
0       8.6
1       7.9
2       7.9

yield kullanarak birden çok satır döndürme

Giriş satırı başına birden çok satır yaymak için reduce=False ayarlayın ve yield kullanın. Çıkış alan adlarını ve türlerini names ve types ile belirtin.

>>> iris.count()
150

>>> def handle(row):
>>>     yield row.sepallength - row.sepalwidth, row.sepallength + row.sepalwidth
>>>     yield row.petallength - row.petalwidth, row.petallength + row.petalwidth

>>> iris.apply(handle, axis=1, names=['iris_add', 'iris_sub'], types=['float', 'float']).count()
300

Çağrı noktalarında tekrar etmekten kaçınmak için çıkış şemasını doğrudan işlev üzerinde açıklamalı olarak belirtin:

>>> from odps.df import output

>>> @output(['iris_add', 'iris_sub'], ['float', 'float'])
>>> def handle(row):
>>>     yield row.sepallength - row.sepalwidth, row.sepallength + row.sepalwidth
>>>     yield row.petallength - row.petalwidth, row.petallength + row.petalwidth

>>> iris.apply(handle, axis=1).count()
300

Eşdeğer: yalnızca map içeren map_reduce

Yalnızca map modundaki map_reduce, axis=1 ile apply işlevine eşdeğerdir:

>>> iris.map_reduce(mapper=handle).count()
300

Mevcut bir UDTF kullanma

MaxCompute'da mevcut bir kullanıcı tanımlı tablo değerli işlevi (UDTF) çağırmak için adını dize olarak iletin:

>>> iris['name', 'sepallength'].apply('your_func', axis=1, names=['name2', 'sepallength2'], types=['string', 'float'])

Satır çıkışını lateral view ile birleştirme

reduce=False olduğunda, UDF çıktısını orijinal sütunlarla lateral view kullanarak birleştirin; bu durum toplamalar için kullanışlıdır:

>>> from odps.df import output

>>> @output(['iris_add', 'iris_sub'], ['float', 'float'])
>>> def handle(row):
>>>     yield row.sepallength - row.sepalwidth, row.sepallength + row.sepalwidth
>>>     yield row.petallength - row.petalwidth, row.petallength + row.petalwidth

>>> iris[iris.category, iris.apply(handle, axis=1)]

Bir sütuna özel toplamlar uygulama

Tüm Sequence nesneleri üzerinde özel bir toplama sınıfını geçirmek için axis=0 (veya axis bağımsız değişkeni olmadan) ile apply kullanın. Sınıf buffer, __call__, merge ve getvalue uygulamalıdır.

class Agg(object):

    def buffer(self):
        return [0.0, 0]

    def __call__(self, buffer, val):
        buffer[0] += val
        buffer[1] += 1

    def merge(self, buffer, pbuffer):
        buffer[0] += pbuffer[0]
        buffer[1] += pbuffer[1]

    def getvalue(self, buffer):
        if buffer[1] == 0:
            return 0.0
        return buffer[0] / buffer[1]
>>> iris.exclude('name').apply(Agg)
   sepallength_aggregation  sepalwidth_aggregation  petallength_aggregation  petalwidth_aggregation
0                 5.843333                   3.054                 3.758667                1.198667

UDF'lerde MaxCompute kaynaklarını okuma

UDF'ler MaxCompute kaynaklarını — dosya kaynakları ve tablo kaynakları — okuyabilir veya bir Collection nesnesini kaynak olarak referans alabilir. Kaynakların satır başına değil, başlatma sırasında bir kez yüklenmesi için UDF'yi bir kapanış veya çağrılabilir sınıfla sarın.

Kaynakları her işlev çağrısında değil, kapanış içinde yüklemek yineleyen başlatma ek yükünü önler; örneğin, arama tabloları veya model yapıtları yüklenirken.

Dosya ve koleksiyon kaynaklarıyla satır düzeyi UDF

>>> file_resource = o.create_resource('pyodps_iris_file', 'file', file_obj='Iris-setosa')

>>> iris_names_collection = iris.distinct('name')[:2]
>>> iris_names_collection
       sepallength
0      Iris-setosa
1  Iris-versicolor
>>> def myfunc(resources):  # resources are passed in by calling order
>>>     names = set()
>>>     fileobj = resources[0]  # file resources are represented by a file-like object
>>>     for l in fileobj:
>>>         names.add(l)
>>>     collection = resources[1]
>>>     for r in collection:
>>>         names.add(r.name)  # retrieve values by field name or offset
>>>     def h(x):
>>>         if x in names:
>>>             return True
>>>         else:
>>>             return False
>>>     return h

>>> df = iris.distinct('name')
>>> df = df[df.name,
>>>         df.name.map(myfunc, resources=[file_resource, iris_names_collection], rtype='boolean').rename('isin')]

>>> df
              name   isin
0      Iris-setosa   True
1  Iris-versicolor   True
2   Iris-virginica  False
Bölümlenmiş tablolar okunurken bölüm alanları dahil edilmez.

Yerel DataFrame'i kaynak olarak kullanan satır düzeyi UDF

Yerel değişkenler, yürütme sırasında MaxCompute'da kaynak olarak referans alınabilir. Aşağıdaki örnekte stop_words, yürütücünün UDF'ye kaynak olarak ilettiği yerel bir DataFrame'dir:

>>> words_df
                     sentence
0                 Hello World
1                Hello Python
2  Life is short I use Python

>>> import pandas as pd
>>> stop_words = DataFrame(pd.DataFrame({'stops': ['is', 'a', 'I']}))

>>> @output(['sentence'], ['string'])
>>> def filter_stops(resources):
>>>     stop_words = set([r[0] for r in resources[0]])
>>>     def h(row):
>>>         return ' '.join(w for w in row[0].split() if w not in stop_words),
>>>     return h

>>> words_df.apply(filter_stops, axis=1, resources=[stop_words])
                sentence
0            Hello World
1           Hello Python
2  Life short use Python
Satır işlemleri için ( axis=1 ), kaynakları yüklemek üzere işlev kapanışı veya çağrılabilir sınıf kullanın. Sütun toplamları için bunun yerine __init__ yöntemini kullanın.

Üçüncü taraf Python kitaplıklarını yükleme

MaxCompute, Python paketlerinin .whl, .egg, .zip ve .tar.gz biçimlerinde yüklenmesini destekler. Tüm bağımlılıklar açıkça belirtilmelidir; bir bağımlılığın atlanması çalışma zamanında içe aktarma hatalarına neden olur.

Paket türüne göre yükleme yolunu seçin:

Paket türü Yükleme yöntemi Notlar
Önceden yüklenmiş Gerek yok Yalnızca NumPy
Saf Python (derlenmiş kod yok, dosya işlemi yok) .whl dosya kaynağı olarak yükleyin python-dateutil, pytz, six gibi paketler için çalışır. MaxCompute'in sonraki sürümleri dosya işlemleri içeren paketleri de destekler.
İkili (derlenmiş C uzantıları) Arşiv kaynağı olarak .zip arşivi yükleyin, yalıtımı etkinleştirin cp27-cp27m-manylinux1_x86_64 platform etiketi gerekir; Linux üzerinde derleyin

Saf Python paketleri

Varsayılan olarak PyODPS, saf Python kodu içeren ancak dosya işlemi olmayan üçüncü taraf kitaplıkları destekler. Aşağıdaki örnek python-dateutil ve bunun bağımlılığı olan six'i yükler.

Adım 1: Paketi ve bağımlılıklarını indirin. Paketler Linux için oluşturulmalıdır.

$ pip download python-dateutil -d /to/path/

Bu işlem six-1.10.0-py2.py3-none-any.whl ve python_dateutil-2.5.3-py2.py3-none-any.whl dosyalarını indirir.

Adım 2: Her iki dosyayı da create_resource kullanarak kaynak olarak yükleyin.

# Make sure that file name extensions are correct.
>>> odps.create_resource('six.whl', 'file', file_obj=open('six-1.10.0-py2.py3-none-any.whl', 'rb'))
>>> odps.create_resource('python_dateutil.whl', 'file', file_obj=open('python_dateutil-2.5.3-py2.py3-none-any.whl', 'rb'))

Adım 3: Kitaplıkları kullanın. Bunları options.df.libraries aracılığıyla genel olarak veya libraries parametresiyle yürütme başına belirtin.

# Global configuration (applies to all subsequent DataFrame operations in this session)
>>> from odps import options

>>> def get_year(t):
>>>     from dateutil.parser import parse
>>>     return parse(t).strftime('%Y')

>>> options.df.libraries = ['six.whl', 'python_dateutil.whl']
>>> df.datestr.map(get_year)
   datestr
0     2016
1     2015
# Per-execution configuration (applies only to this call)
>>> def get_year(t):
>>>     from dateutil.parser import parse
>>>     return parse(t).strftime('%Y')

>>> df.datestr.map(get_year).execute(libraries=['six.whl', 'python_dateutil.whl'])
   datestr
0     2016
1     2015

İkili paketler (derlenmiş kod içeren)

Derlenmiş C uzantıları içeren paketler (SciPy veya pandas gibi) ek adımlar gerektirir:

  • .whl dosyası cp27-cp27m-manylinux1_x86_64 platform etiketini kullanmalıdır.

  • Dosyayı, .whl uzantısı .zip olarak yeniden adlandırılarak arşiv kaynağı olarak yükleyin.

  • odps.isolation.session.enable değerini True olarak ayarlayın veya proje ayarlarınızda isolation özelliğini etkinleştirin.

# Upload the binary package as an archive with the .zip extension.
>>> odps.create_resource('scipy.zip', 'archive', file_obj=open('scipy-0.19.0-cp27-cp27m-manylinux1_x86_64.whl', 'rb'))

# If isolation is already enabled in your project, the following option is optional.
>>> options.sql.settings = {'odps.isolation.session.enable': True}

>>> def psi(value):
>>>     # Import the third-party library inside the function to avoid errors
>>>     # caused by structural differences between operating systems.
>>>     from scipy.special import psi
>>>     return float(psi(value))

>>> df.float_col.map(psi).execute(libraries=['scipy.zip'])

Kaynaktan ikili bir paket oluşturmak için aşağıdaki komutu bir Linux kabuğunda çalıştırın. macOS veya Windows üzerinde oluşturulan Wheel dosyaları MaxCompute ile uyumlu değildir.

python setup.py bdist_wheel

MaxCompute konsolu üzerinden yükleme

PyODPS API'sine alternatif olarak, paketleri MaxCompute konsolunda add archive kullanarak yükleyin.

Adım 1: Her bağımlılık için doğru paket dosyasını belirleyin.

Çoğu paket birden çok platform için .whl dosyaları sağlar. İkili paketler için adında cp27-cp27m-manylinux1_x86_64 geçen dosyayı bulun. Saf Python paketleri için herhangi bir py2.py3-none-any wheel çalışır.

Adım 2: Gerekli tüm bağımlılıkları doğrulayın. Aşağıdaki tabloda yaygın paketlerin bağımlılıkları listelenmiştir.

Paket Bağımlılıklar
pandas NumPy, python-dateutil, pytz, six
SciPy NumPy
scikit-learn NumPy, SciPy
NumPy zaten önceden yüklenmiştir. Yalnızca python-dateutil, pytz, pandas, SciPy, scikit-learn ve six'i yükleyin.

Adım 3: Paket dosyalarını indirin. Aşağıdaki tabloda her paket için indirilecek belirli dosyalar listelenmiştir.

Paket İndirilecek dosya Kaynak adını yükle
python-dateutil python-dateutil-2.6.0.zip python-dateutil.zip
pytz pytz-2017.2.zip pytz.zip
six six-1.11.0.tar.gz six.tar.gz
pandas pandas-0.20.2-cp27-cp27m-manylinux1_x86_64.zip pandas.zip
SciPy scipy-0.19.0-cp27-cp27m-manylinux1_x86_64.zip scipy.zip
scikit-learn scikit_learn-0.18.1-cp27-cp27m-manylinux1_x86_64.zip sklearn.zip

Adım 4: Her dosyayı yükleyin. İkili paketler (pandas, SciPy, scikit-learn) için yüklemeden önce .whl uzantısını .zip olarak yeniden adlandırın.

add archive python-dateutil.zip;
add archive pandas.zip;

Yürütme için kitaplıkları belirtme

Oturum için genel olarak kitaplıkları ayarlamak üzere options.df.libraries kullanın veya bunu tek bir çağrıyla sınırlamak için libraries parametresini doğrudan bir yürütme yöntemine iletin.

# Global: applies to all subsequent DataFrame operations in this session
>>> from odps import options
>>> options.df.libraries = ['six.whl', 'python_dateutil.whl']
# Local: applies only to this execution call
>>> df.apply(my_func, axis=1).to_pandas(libraries=['six.whl', 'python_dateutil.whl'])