{"id":13429797,"url":"https://github.com/zero-one-group/geni","last_synced_at":"2025-04-04T07:05:48.253Z","repository":{"id":40485522,"uuid":"256728511","full_name":"zero-one-group/geni","owner":"zero-one-group","description":"A Clojure dataframe library that runs on Spark","archived":false,"fork":false,"pushed_at":"2023-11-28T17:22:38.000Z","size":1953,"stargazers_count":292,"open_issues_count":17,"forks_count":27,"subscribers_count":12,"default_branch":"develop","last_synced_at":"2025-03-28T06:06:08.245Z","etag":null,"topics":["big-data","clojure","clojure-library","clojure-repl","data-engineering","data-science","dataframe","distributed-computing","high-performance-computing","machine-learning","parallel-computing","spark"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zero-one-group.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-04-18T10:46:14.000Z","updated_at":"2025-03-20T16:56:12.000Z","dependencies_parsed_at":"2022-08-09T21:51:11.604Z","dependency_job_id":"ee42eaeb-81ca-4dfc-a8da-5a21187c8a2e","html_url":"https://github.com/zero-one-group/geni","commit_stats":{"total_commits":315,"total_committers":14,"mean_commits":22.5,"dds":"0.11746031746031749","last_synced_commit":"e6eaeb937d7598648e1986928d35f5bea336441f"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zero-one-group%2Fgeni","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zero-one-group%2Fgeni/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zero-one-group%2Fgeni/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zero-one-group%2Fgeni/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zero-one-group","download_url":"https://codeload.github.com/zero-one-group/geni/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247135142,"owners_count":20889420,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["big-data","clojure","clojure-library","clojure-repl","data-engineering","data-science","dataframe","distributed-computing","high-performance-computing","machine-learning","parallel-computing","spark"],"created_at":"2024-07-31T02:00:45.680Z","updated_at":"2025-04-04T07:05:48.227Z","avatar_url":"https://github.com/zero-one-group.png","language":"Clojure","funding_links":[],"categories":["Clojure","数据科学"],"sub_categories":["[Tools](#tools-1)"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"logo/geni.png\" width=\"375px\"\u003e\n\u003c/p\u003e\n\nGeni (*/gɜni/* or \"gurney\" without the r) is a [Clojure](https://clojure.org/) dataframe library that runs on [Apache Spark](https://spark.apache.org/). The name means \"fire\" in Javanese.\n\n[![CI](https://github.com/zero-one-group/geni/actions/workflows/continuous-integration.yml/badge.svg?branch=develop)](https://github.com/zero-one-group/geni/actions)\n[![Code Coverage](https://codecov.io/gh/zero-one-group/geni/branch/develop/graph/badge.svg)](https://codecov.io/gh/zero-one-group/geni)\n[![Clojars Project](https://img.shields.io/clojars/v/zero.one/geni.svg)](http://clojars.org/zero.one/geni)\n[![License](https://img.shields.io/github/license/zero-one-group/geni.svg)](LICENSE)\n\n## Overview\n\nGeni provides an idiomatic Spark interface for Clojure without the hassle of Java or Scala interop. Geni uses Clojure's `-\u003e` threading macro as the main way to compose Spark's `Dataset` and `Column` operations in place of the usual method chaining in Scala. It also provides a greater degree of dynamism by allowing args of mixed types such as columns, strings and keywords in a single function invocation. See the docs section on [Geni semantics](docs/semantics.md) for more details.\n\n## Resources\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth align=\"center\" width=\"441\"\u003e\n        Docs\n      \u003c/th\u003e\n      \u003cth align=\"center\" width=\"441\"\u003e\n        Cookbook\n      \u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cul\u003e\n            \u003cli\u003e\u003ca href=\"docs/simple_performance_benchmark.md\"\u003eA Simple Performance Benchmark\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"CODE_OF_CONDUCT.md\"\u003eCode of Conduct\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"CONTRIBUTING.md\"\u003eContributing Guide\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/creating_spark_schemas.md\"\u003eCreating Spark Schemas\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/examples.md\"\u003eExamples\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/design_goals.md\"\u003eDesign Goals\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/semantics.md\"\u003eGeni Semantics\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/manual_dataset_creation.md\"\u003eManual Dataset Creation\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/xgboost.md\"\u003eOptional XGBoost Support\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/pandas_numpy_and_other_idioms.md\"\u003ePandas, NumPy and Other Idioms\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/dataproc.md\"\u003eUsing Dataproc\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/kubernetes_basic.md\"\u003eUsing Kubernetes\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/spark_session.md\"\u003eWhere's The Spark Session\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/why.md\"\u003eWhy?\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/sql_maps.md\"\u003eWorking with SQL Maps\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/collect.md\"\u003eCollecting Data from Spark Datasets\u003c/a\u003e\u003c/li\u003e\n        \u003c/ul\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n        \u003col start=\"0\"\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_00_getting_started_with_clojure_geni_and_spark.md\"\u003e\n                Getting Started with Clojure, Geni and Spark\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_01_reading_and_writing_datasets.md\"\u003e\n                Reading and Writing Datasets\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_02_selecting_rows_and_columns.md\"\u003e\n                Selecting Rows and Columns\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_03_grouping_and_aggregating.md\"\u003e\n                Grouping and Aggregating\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_04_combining_datasets_with_joins_and_unions.md\"\u003e\n                Combining Datasets with Joins and Unions\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_05_string_operations.md\"\u003e\n                String Operations\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_06_cleaning_up_messy_data.md\"\u003e\n                Cleaning up Messy Data\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_07_timestamps_and_dates.md\"\u003e\n                Timestamps and Dates\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_08_window_functions.md\"\u003e\n                Window Functions\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_09_reading_from_and_writing_to_sql_databases.md\"\u003e\n                Reading from and Writing to SQL Databases\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_10_avoiding_repeated_computations_with_caching.md\"\u003e\n                Avoiding Repeated Computations with Caching\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_11_basic_ml_pipelines.md\"\u003e\n                Basic ML Pipelines\n            \u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"docs/cookbook/part_12_customer_segmentation_with_nmf.md\"\u003e\n                Customer Segmentation with NMF\n            \u003c/a\u003e\u003c/li\u003e\n        \u003c/ol\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\n[![cljdoc](https://cljdoc.org/badge/zero.one/geni)](https://cljdoc.org/d/zero.one/geni/CURRENT)\n[![slack](https://badgen.net/badge/-/clojurians%2Fgeni?icon=slack\u0026label)](https://clojurians.slack.com/messages/geni/)\n[![zulip](https://img.shields.io/badge/zulip-clojurians%2Fgeni-brightgreen.svg)](https://clojurians.zulipchat.com/#narrow/stream/256615-geni)\n\n## Basic Examples\n\nAll examples below use the Statlib California housing prices data available for free on [Kaggle](https://www.kaggle.com/camnugent/california-housing-prices).\n\nSpark SQL API for data wrangling:\n\n```clojure\n(require '[zero-one.geni.core :as g])\n\n(def dataframe (g/read-parquet! \"test/resources/housing.parquet\"))\n\n(g/count dataframe)\n=\u003e 5000\n\n(g/print-schema dataframe)\n; root\n;  |-- longitude: double (nullable = true)\n;  |-- latitude: double (nullable = true)\n;  |-- housing_median_age: double (nullable = true)\n;  |-- total_rooms: double (nullable = true)\n;  |-- total_bedrooms: double (nullable = true)\n;  |-- population: double (nullable = true)\n;  |-- households: double (nullable = true)\n;  |-- median_income: double (nullable = true)\n;  |-- median_house_value: double (nullable = true)\n;  |-- ocean_proximity: string (nullable = true)\n\n(-\u003e dataframe (g/limit 5) g/show)\n; +---------+--------+------------------+-----------+--------------+----------+----------+-------------+------------------+---------------+\n; |longitude|latitude|housing_median_age|total_rooms|total_bedrooms|population|households|median_income|median_house_value|ocean_proximity|\n; +---------+--------+------------------+-----------+--------------+----------+----------+-------------+------------------+---------------+\n; |-122.23  |37.88   |41.0              |880.0      |129.0         |322.0     |126.0     |8.3252       |452600.0          |NEAR BAY       |\n; |-122.22  |37.86   |21.0              |7099.0     |1106.0        |2401.0    |1138.0    |8.3014       |358500.0          |NEAR BAY       |\n; |-122.24  |37.85   |52.0              |1467.0     |190.0         |496.0     |177.0     |7.2574       |352100.0          |NEAR BAY       |\n; |-122.25  |37.85   |52.0              |1274.0     |235.0         |558.0     |219.0     |5.6431       |341300.0          |NEAR BAY       |\n; |-122.25  |37.85   |52.0              |1627.0     |280.0         |565.0     |259.0     |3.8462       |342200.0          |NEAR BAY       |\n; +---------+--------+------------------+-----------+--------------+----------+----------+-------------+------------------+---------------+\n\n(-\u003e dataframe (g/describe :housing_median_age :total_rooms :population) g/show)\n; +-------+------------------+------------------+-----------------+\n; |summary|housing_median_age|total_rooms       |population       |\n; +-------+------------------+------------------+-----------------+\n; |count  |5000              |5000              |5000             |\n; |mean   |30.9842           |2393.2132         |1334.9684        |\n; |stddev |12.969656616832669|1812.4457510408017|954.0206427949117|\n; |min    |1.0               |1000.0            |100.0            |\n; |max    |9.0               |999.0             |999.0            |\n; +-------+------------------+------------------+-----------------+\n\n(-\u003e dataframe\n    (g/group-by :ocean_proximity)\n    (g/agg {:count        (g/count \"*\")\n            :mean-rooms   (g/mean :total_rooms)\n            :distinct-lat (g/count-distinct (g/int :latitude))})\n    (g/order-by (g/desc :count))\n    g/show)\n; +---------------+-----+------------------+------------+\n; |ocean_proximity|count|mean-rooms        |distinct-lat|\n; +---------------+-----+------------------+------------+\n; |INLAND         |1823 |2358.181020296215 |10          |\n; |\u003c1H OCEAN      |1783 |2467.5361749859785|7           |\n; |NEAR BAY       |1287 |2368.72027972028  |2           |\n; |NEAR OCEAN     |107  |2046.1869158878505|2           |\n; +---------------+-----+------------------+------------+\n\n(-\u003e dataframe\n    (g/select {:ocean :ocean_proximity\n               :house (g/struct {:rooms (g/struct :total_rooms :total_bedrooms)\n                                 :age   :housing_median_age})\n               :coord (g/struct {:lat :latitude :long :longitude})})\n    (g/limit 3)\n    g/collect)\n=\u003e ({:ocean \"NEAR BAY\",\n     :house {:rooms {:total_rooms 880.0, :total_bedrooms 129.0}, \n             :age 41.0},\n     :coord {:lat 37.88, :long -122.23}}\n    {:ocean \"NEAR BAY\",\n     :house {:rooms {:total_rooms 7099.0, :total_bedrooms 1106.0}, \n             :age 21.0},\n     :coord {:lat 37.86, :long -122.22}}\n    {:ocean \"NEAR BAY\",\n     :house {:rooms {:total_rooms 1467.0, :total_bedrooms 190.0}, \n             :age 52.0},\n     :coord {:lat 37.85, :long -122.24}})\n```\n\nSpark ML example translated from [Spark's programming guide](https://spark.apache.org/docs/latest/ml-pipeline.html):\n\n```clojure\n(require '[zero-one.geni.core :as g])\n(require '[zero-one.geni.ml :as ml])\n\n(def training-set\n  (g/table-\u003edataset\n    [[0 \"a b c d e spark\"  1.0]\n     [1 \"b d\"              0.0]\n     [2 \"spark f g h\"      1.0]\n     [3 \"hadoop mapreduce\" 0.0]]\n    [:id :text :label]))\n\n(def pipeline\n  (ml/pipeline\n    (ml/tokenizer {:input-col :text\n                   :output-col :words})\n    (ml/hashing-tf {:num-features 1000\n                    :input-col :words\n                    :output-col :features})\n    (ml/logistic-regression {:max-iter 10\n                             :reg-param 0.001})))\n\n(def model (ml/fit training-set pipeline))\n\n(def test-set\n  (g/table-\u003edataset\n    [[4 \"spark i j k\"]\n     [5 \"l m n\"]\n     [6 \"spark hadoop spark\"]\n     [7 \"apache hadoop\"]]\n    [:id :text]))\n\n(-\u003e test-set\n    (ml/transform model)\n    (g/select :id :text :probability :prediction)\n    g/show)\n;; +---+------------------+----------------------------------------+----------+\n;; |id |text              |probability                             |prediction|\n;; +---+------------------+----------------------------------------+----------+\n;; |4  |spark i j k       |[0.1596407738787411,0.8403592261212589] |1.0       |\n;; |5  |l m n             |[0.8378325685476612,0.16216743145233883]|0.0       |\n;; |6  |spark hadoop spark|[0.0692663313297627,0.9307336686702373] |1.0       |\n;; |7  |apache hadoop     |[0.9821575333444208,0.01784246665557917]|0.0       |\n;; +---+------------------+----------------------------------------+----------+\n```\n\nMore detailed examples can be found [here](examples/README.md).\n\n## Quick Start\n\n### Install Geni\n\nInstall the `geni` script to `/usr/local/bin` with:\n\n```bash\nwget https://raw.githubusercontent.com/zero-one-group/geni/develop/scripts/geni\nchmod a+x geni\nsudo mv geni /usr/local/bin/\n```\n\nThe command `geni` downloads the latest Geni uberjar and places it in `~/.geni/geni-repl-uberjar.jar`, and runs it with `java -jar`.\n\n### Uberjar\n\nDownload the latest Geni REPL uberjar from the [release](https://github.com/zero-one-group/geni/releases) page. Run the uberjar as follows:\n\n```bash\njava -jar \u003cuberjar-name\u003e\n```\n\nThe uberjar app prints the default `SparkSession` instance, starts an nREPL server with an `.nrepl-port` file for easy text-editor connection and steps into a Clojure REPL(-y).\n\n### Leiningen Template\n\nUse [Leiningen](http://leiningen.org/) to create a [template](https://github.com/zero-one-group/geni-template) of a Geni project:\n\n```bash\nlein new geni \u003cproject-name\u003e\n```\n\n`cd` into the project directory and do `lein run`. The templated app runs a Spark ML example, and then steps into a Clojure REPL-y with an `.nrepl-port` file.\n\n### Screencast Demos\n\n\u003ctable\u003e\n    \u003ctr\u003e\n        \u003cth\u003eInstall\u003c/th\u003e\n        \u003cth\u003eUberjar\u003c/th\u003e\n        \u003cth\u003eLeiningen\u003c/th\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd\u003e \u003ca href=\"https://asciinema.org/a/352552?t=1\u0026theme=monokai\u0026speed=1.75\"\u003e\u003cimg src=\"https://asciinema.org/a/352552.svg\"/\u003e\u003c/a\u003e \u003c/td\u003e\n        \u003ctd\u003e \u003ca href=\"https://asciinema.org/a/352138?t=1\u0026theme=monokai\u0026speed=1.75\"\u003e\u003cimg src=\"https://asciinema.org/a/352138.svg\"/\u003e\u003c/a\u003e \u003c/td\u003e\n        \u003ctd\u003e \u003ca href=\"https://asciinema.org/a/349721?t=1\u0026theme=monokai\u0026speed=1.75\"\u003e\u003cimg src=\"https://asciinema.org/a/349721.svg\"/\u003e\u003c/a\u003e \u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n\n## Installation\n\nAdd the following to your `project.clj` dependency:\n\n[![Clojars Project](https://clojars.org/zero.one/geni/latest-version.svg)](http://clojars.org/zero.one/geni)\n\nYou would also need to add Spark as provided dependencies. For instance, have the following key-value pair for the `:profiles` map:\n\n```clojure\n:provided\n{:dependencies [;; Spark\n                [org.apache.spark/spark-avro_2.12 \"3.3.3\"]\n                [org.apache.spark/spark-core_2.12 \"3.3.3\"]\n                [org.apache.spark/spark-hive_2.12 \"3.3.3\"]\n                [org.apache.spark/spark-mllib_2.12 \"3.3.3\"]\n                [org.apache.spark/spark-sql_2.12 \"3.3.3\"]\n                [org.apache.spark/spark-streaming_2.12 \"3.3.3\"]\n                ; Arrow\n                [org.apache.arrow/arrow-memory-netty \"4.0.0\"]\n                [org.apache.arrow/arrow-memory-core \"4.0.0\"]\n                [org.apache.arrow/arrow-vector \"4.0.0\"\n                :exclusions [commons-codec com.fasterxml.jackson.core/jackson-databind]]\n                ;; Databases\n                [mysql/mysql-connector-java \"8.0.25\"]\n                [org.postgresql/postgresql \"42.2.20\"]\n                [org.xerial/sqlite-jdbc \"3.34.0\"]\n                ;; Optional: Spark XGBoost\n                [ml.dmlc/xgboost4j-spark_2.12 \"1.2.0\"]\n                [ml.dmlc/xgboost4j_2.12 \"1.2.0\"]]}\n```\n\nYou may also need to install `libatlas3-base` and `libopenblas-base` to use a native BLAS, and install `libgomp1` to train XGBoost4J models. When the optional dependencies are not present, the vars to the corresponding functions (such as `ml/xgboost-classifier`) will be left unbound.\n\n## License\n\nCopyright 2020 Zero One Group.\n\nGeni is licensed under Apache License v2.0, see [LICENSE](LICENSE).\n\n## Mentions\n\nSome parts of the project have been taken from or inspired by:\n\n* [finagle-clojure](https://github.com/finagle/finagle-clojure) for Scala interop functions.\n* [LispCast](https://lispcast.com/) for [exponential backoff](https://lispcast.com/exponential-backoff/).\n* Reddit users [/u/borkdude](https://old.reddit.com/user/borkdude) and [/u/czan](https://old.reddit.com/user/czan) for [with-dynamic-import](src/zero_one/geni/utils.clj).\n* StackOverflow user [whocaresanyway's answer](https://stackoverflow.com/questions/1696693/clojure-how-to-find-out-the-arity-of-function-at-runtime) for `arg-count`.\n* [Julia Evans'](https://jvns.ca/) [Pandas Cookbook](https://github.com/jvns/pandas-cookbook) for its syllabus.\n* Reddit user [/u/joinr](https://old.reddit.com/user/joinr) for helping with [unit-testing the REPL](test/zero_one/geni/main_test.clj).\n* [Sparkling](https://github.com/gorillalabs/sparkling), [sparkplug](https://github.com/amperity/sparkplug) and [Gabriel Borges](https://github.com/borgesgabriel) for helping with the RDD function serialisation.\n* [Chris Nuernberger](https://github.com/cnuernber) and [Tomasz Sulej](https://github.com/tsulej) for helping with [tech.ml.dataset](https://github.com/techascent/tech.ml.dataset) and [tablecloth](https://github.com/scicloj/tablecloth).\n* [Ubuntu](https://ubuntu.com/community/code-of-conduct), [Django](https://www.djangoproject.com/conduct/) and [Conjure](https://github.com/Olical/conjure/blob/master/.github/CODE_OF_CONDUCT.md) for their codes of conduct.\n* [FZF](https://github.com/junegunn/fzf) for their issue template.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzero-one-group%2Fgeni","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzero-one-group%2Fgeni","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzero-one-group%2Fgeni/lists"}