Package: pyduct
PyDuctA framework for building and running simple data engineering pipelines in Python.In Data Science or Data Engineering you constantly hear term “data pipeline”. But there are so many meanings to this term and people often are refering to very specific tools or packages depending on their own background/needs. There are pipelines for pretty much everything and in Python alone I can think ofLuigi,Airflow,scikit-learn pipelines, andPandas pipesjust off the top of my head -this articledoes a good job of helping you understand what is out there.It can be quite confusing especially if you want a simple and agnostic pipeline that you can customize for your specific needs with no bells and whistles or lock-ins to libraries etc. That is where PyDuct comes in. It is for the simple data engineer who just wants to get stuff done in an ordered and repeatable way.PyDuct is a simple data pipeline that automates a chain of transformations performed on some data.PyDuct data pipelines are a great way of introducing automation, reproducibility, structure, and flow to your data engineering projects.PyDuct was made byRobert JohnsonandAlexander KozlovandMohammadreza KhanarmueiWhat is it?The PyDuct transformation pipelines use user defined transformation functions linked together into a TransformationPipe. The key feature of PyDuct is that the datasource passed in can be almost anything that you desire  - e.g. a pandas dataframe, a geopandas dataframe, and iris datacube, a numppy array, so long as your transformation steps read and write the same object PyDuct will work for you.Installpip install pyductHow to useThe TransformationPipe class accepts a list of transformation functions,'steps', to be applied sequentially. Each step contains a name and a function, applied to the input DataObject and will return a transformed DataObject. There is also a third argument in a step that is an optional dictionary of parameters to be passed to your step transformation functions.In order to use PyDuct you need two things - a DataObject and a set of transformation stepsDataObjectIn this very simplified example we will use ageopandas.GeoDataFrameas our input DataObject. To do this we will load an example data set fromKaggleon the global distribution of Volcano Eruptions:https://www.kaggle.com/datasets/texasdave/volcano-eruptionsthat we have stored in the repo for this package as 'volcano_data_2010.csv'importpandasimportgeopandasLoad the data and put it into a geopandas dataframe:df1=pandas.read_csv('../test_data/volcano_data_2010.csv')# Keep only relevant columnsdf=df1.loc[:,("Year","Name","Country","Latitude","Longitude","Type")]# Create point geometriesgeometry=geopandas.points_from_xy(df.Longitude,df.Latitude)geo_df=geopandas.GeoDataFrame(df[['Year','Name','Country','Latitude','Longitude','Type']],geometry=geometry)geo_df.head()<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}</style>YearNameCountryLatitudeLongitudeTypegeometry02010TungurahuaEcuador-1.467-78.442StratovolcanoPOINT (-78.44200 -1.46700)12010EyjafjallajokullIceland63.630-19.620StratovolcanoPOINT (-19.62000 63.63000)22010PacayaGuatemala14.381-90.601Complex volcanoPOINT (-90.60100 14.38100)32010SariganUnited States16.708145.780StratovolcanoPOINT (145.78000 16.70800)42010Karangetang [Api Siau]Indonesia2.780125.480StratovolcanoPOINT (125.48000 2.78000)StepsJust as an example of something to do we will define only one transformation steps to spatially subset to the Australian region. Yes, i know that this is an unrealistic example but it is just here to show you how to implement pipelines.We must now write our transformation function - keep in mind that the function must take our DataObject as an input and return a transformed DataObject as a return... in this example that is a geopandas.GeoDataFramefrompyprojimportcrsfromshapely.geometryimportPolygon,MultiPolygon,box,PointdefspatialCrop(gdf:geopandas.GeoDataFrame,**kwargs):"""This function will apply a sptial limit to a GeoDataFrame based on user-defined limits.----------parameters:gdf (geopandas.GeoDataFrame): an input GeoDataFramekwargs (dict): parameters,- boundingBox (list): an iterable (lon_min, lat_min, lon_max, lat_max) of the specified region.Output:transformed_gdf (gdp.GeoDataFrame): GeoDataFrame that is spatially limited to the boundingBox."""if"boundingBox"notinkwargs:returngdfboundingBox=kwargs["boundingBox"]# just an example so we are doing naughty things with the CRS... look away here...coord_system=crs.crs.CRS('WGS 84')bounding=geopandas.GeoDataFrame({'limit':['bounding box'],'geometry':[box(boundingBox[0],boundingBox[1],boundingBox[2],boundingBox[3])]},crs=coord_system)limited_gdf=geopandas.tools.sjoin(gdf,bounding,op='intersects',how='left')limited_gdf=limited_gdf[limited_gdf['limit']=='bounding box']limited_gdf=limited_gdf.drop(columns=['index_right','limit'])returnlimited_gdfDefine a PyDuct PipeNow that we have a step or function and some data we can now define our transformation pipeline:pipe=TransformationPipe(steps=[('refine region',spatialCrop,{"boundingBox":[80,-50,180,0]})])Evaluate your PyDuct PipeThis where things get interesting... we can now callevaluateon our pipe and watch the magic happen:Input data:geo_df<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}</style>YearNameCountryLatitudeLongitudeTypegeometry02010TungurahuaEcuador-1.467-78.442StratovolcanoPOINT (-78.44200 -1.46700)12010EyjafjallajokullIceland63.630-19.620StratovolcanoPOINT (-19.62000 63.63000)22010PacayaGuatemala14.381-90.601Complex volcanoPOINT (-90.60100 14.38100)32010SariganUnited States16.708145.780StratovolcanoPOINT (145.78000 16.70800)42010Karangetang [Api Siau]Indonesia2.780125.480StratovolcanoPOINT (125.48000 2.78000)........................582018KilaueaUnited States19.425-155.292Shield volcanoPOINT (-155.29200 19.42500)592018KadovarPapua New Guinea-3.620144.620StratovolcanoPOINT (144.62000 -3.62000)602018IjenIndonesia-8.058114.242StratovolcanoPOINT (114.24200 -8.05800)612018KilaueaUnited States19.425-155.292Shield volcanoPOINT (-155.29200 19.42500)622018AobaVanuatu-15.400167.830Shield volcanoPOINT (167.83000 -15.40000)63 rows × 7 columnsEvaluation:transformed_geo_df=pipe.evaluate(geo_df)Transformed data:transformed_geo_df<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}</style>YearNameCountryLatitudeLongitudeTypegeometry62010MerapiIndonesia-7.542110.442StratovolcanoPOINT (110.44200 -7.54200)82010Tengger CalderaIndonesia-7.942112.950StratovolcanoPOINT (112.95000 -7.94200)92011MerapiIndonesia-7.542110.442StratovolcanoPOINT (110.44200 -7.54200)222013MerapiIndonesia-7.542110.442StratovolcanoPOINT (110.44200 -7.54200)232013PaluwehIndonesia-8.320121.708StratovolcanoPOINT (121.70800 -8.32000)252013PaluwehIndonesia-8.320121.708StratovolcanoPOINT (121.70800 -8.32000)292013OkatainaNew Zealand-38.120176.500Lava domePOINT (176.50000 -38.12000)312014KelutIndonesia-7.930112.308StratovolcanoPOINT (112.30800 -7.93000)392015ManamPapua New Guinea-4.100145.061StratovolcanoPOINT (145.06100 -4.10000)412015OkatainaNew Zealand-38.120176.500Lava domePOINT (176.50000 -38.12000)452016RinjaniIndonesia-8.420116.470StratovolcanoPOINT (116.47000 -8.42000)502017Dieng Volc ComplexIndonesia-7.200109.920Complex volcanoPOINT (109.92000 -7.20000)522017AobaVanuatu-15.400167.830Shield volcanoPOINT (167.83000 -15.40000)532017MerapiIndonesia-7.542110.442StratovolcanoPOINT (110.44200 -7.54200)552018KadovarPapua New Guinea-3.620144.620StratovolcanoPOINT (144.62000 -3.62000)592018KadovarPapua New Guinea-3.620144.620StratovolcanoPOINT (144.62000 -3.62000)602018IjenIndonesia-8.058114.242StratovolcanoPOINT (114.24200 -8.05800)622018AobaVanuatu-15.400167.830Shield volcanoPOINT (167.83000 -15.40000)The power of this work is in its reproducibility and scalablilty.CreditsLogo art from "Vecteezy.com"Demo data from "Kaggle.com"