مشاركة عبر


العمل مع DataFrames والجداول في R

توضح هذه المقالة كيفية استخدام حزم R مثل SparkR وsparklyr وdplyrللعمل مع R data.frames وSpark DataFrames والجداول في الذاكرة.

لاحظ أنه أثناء العمل مع SparkR وsparklyr وdplyr، قد تجد أنه يمكنك إكمال عملية معينة مع كل هذه الحزم، ويمكنك استخدام الحزمة الأكثر راحة مع. على سبيل المثال، لتشغيل استعلام، يمكنك استدعاء دالات مثل SparkR::sqlو sparklyr::sdf_sqlو.dplyr::select في أوقات أخرى، قد تتمكن من إكمال عملية باستخدام حزمة واحدة أو حزمتين فقط، وتعتمد العملية التي تختارها على سيناريو الاستخدام الخاص بك. على سبيل المثال، تختلف الطريقة التي تستدعيها sparklyr::sdf_quantile قليلا عن الطريقة التي تستدعي dplyr::percentile_approxبها ، على الرغم من أن كلتا الدالتين تحددان الكمية.

يمكنك استخدام SQL كجسر بين SparkR وsparklyr. على سبيل المثال، يمكنك استخدام SparkR::sql للاستعلام عن الجداول التي تقوم بإنشائها باستخدام sparklyr. يمكنك استخدام sparklyr::sdf_sql للاستعلام عن الجداول التي تقوم بإنشائها باستخدام SparkR. ويتم dplyr دائما ترجمة التعليمات البرمجية إلى SQL في الذاكرة قبل تشغيلها. راجع أيضا إمكانية التشغيل التفاعلي لواجهة برمجة التطبيقات وترجمة SQL.

تحميل SparkR وsparklyr وdplyr

يتم تضمين حزم SparkR وsparklyr وdplyr في Databricks Runtime المثبت على مجموعات Azure Databricks. لذلك، لا تحتاج إلى استدعاء المعتاد install.package قبل أن تتمكن من البدء في استدعاء هذه الحزم. ومع ذلك، يجب عليك تحميل هذه الحزم أولا library . على سبيل المثال، من داخل دفتر ملاحظات R في مساحة عمل Azure Databricks، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لتحميل SparkR وsparklyr وdplyr:

library(SparkR)
library(sparklyr)
library(dplyr)

الاتصال sparklyr إلى نظام مجموعة

بعد تحميل sparklyr، يجب استدعاء sparklyr::spark_connect للاتصال بالمجموعة، مع تحديد databricks أسلوب الاتصال. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات للاتصال بالمجموعة التي تستضيف دفتر الملاحظات:

sc <- spark_connect(method = "databricks")

في المقابل، يقوم دفتر ملاحظات Azure Databricks بالفعل بإنشاء SparkSession على نظام المجموعة للاستخدام مع SparkR، لذلك لا تحتاج إلى الاتصال SparkR::sparkR.session قبل أن تتمكن من البدء في استدعاء SparkR.

تحميل ملف بيانات JSON إلى مساحة العمل الخاصة بك

تستند العديد من أمثلة التعليمات البرمجية في هذه المقالة إلى البيانات الموجودة في موقع معين في مساحة عمل Azure Databricks، مع أسماء أعمدة وأنواع بيانات محددة. تنشأ البيانات الخاصة بمثال التعليمات البرمجية هذا في ملف JSON يسمى book.json من داخل GitHub. للحصول على هذا الملف وتحميله إلى مساحة العمل الخاصة بك:

  1. انتقل إلى ملف books.json على GitHub واستخدم محرر نص لنسخ محتوياته إلى ملف يسمى books.json في مكان ما على جهازك المحلي.
  2. في الشريط الجانبي لمساحة عمل Azure Databricks، انقر فوق كتالوج.
  3. انقر فوق إنشاء جدول.
  4. في علامة التبويب تحميل ملف ، أفلت books.json الملف من الجهاز المحلي إلى المربع إسقاط الملفات لتحميلها . أو حدد النقر للاستعراض، والاستعراض وصولا إلى books.json الملف من جهازك المحلي.

بشكل افتراضي، يقوم Azure Databricks بتحميل ملفك المحلي books.json إلى موقع DBFS في مساحة العمل الخاصة بك باستخدام المسار /FileStore/tables/books.json.

لا تنقر فوق إنشاء جدول باستخدام واجهة المستخدم أو إنشاء جدول في دفتر الملاحظات. تستخدم أمثلة التعليمات البرمجية في هذه المقالة البيانات الموجودة في الملف الذي تم تحميله books.json في موقع DBFS هذا.

قراءة بيانات JSON في DataFrame

يستخدم sparklyr::spark_read_json لقراءة ملف JSON الذي تم تحميله في DataFrame، مع تحديد الاتصال والمسار إلى ملف JSON واسم لتمثيل الجدول الداخلي للبيانات. في هذا المثال، يجب تحديد أن book.json الملف يحتوي على أسطر متعددة. تحديد مخطط الأعمدة هنا اختياري. وإلا، يستنتج sparklyr مخطط الأعمدة بشكل افتراضي. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لقراءة بيانات ملف JSON الذي تم تحميله في DataFrame المسمى jsonDF:

jsonDF <- spark_read_json(
  sc      = sc,
  name    = "jsonTable",
  path    = "/FileStore/tables/books.json",
  options = list("multiLine" = TRUE),
  columns = c(
    author    = "character",
    country   = "character",
    imageLink = "character",
    language  = "character",
    link      = "character",
    pages     = "integer",
    title     = "character",
    year      = "integer"
  )
)

يمكنك استخدام SparkR::headأو SparkR::showأو sparklyr::collect لطباعة الصفوف الأولى من DataFrame. بشكل افتراضي، head يطبع الصفوف الستة الأولى بشكل افتراضي. show وطباعة collect أول 10 صفوف. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لطباعة الصفوف الأولى من DataFrame المسماة jsonDF:

head(jsonDF)

# Source: spark<?> [?? x 8]
#   author                  country        image…¹ langu…² link  pages title  year
#   <chr>                   <chr>          <chr>   <chr>   <chr> <int> <chr> <int>
# 1 Chinua Achebe           Nigeria        images… English "htt…   209 Thin…  1958
# 2 Hans Christian Andersen Denmark        images… Danish  "htt…   784 Fair…  1836
# 3 Dante Alighieri         Italy          images… Italian "htt…   928 The …  1315
# 4 Unknown                 Sumer and Akk… images… Akkadi… "htt…   160 The … -1700
# 5 Unknown                 Achaemenid Em… images… Hebrew  "htt…   176 The …  -600
# 6 Unknown                 India/Iran/Ir… images… Arabic  "htt…   288 One …  1200
# … with abbreviated variable names ¹​imageLink, ²​language

show(jsonDF)

# Source: spark<jsonTable> [?? x 8]
#    author                  country       image…¹ langu…² link  pages title  year
#    <chr>                   <chr>         <chr>   <chr>   <chr> <int> <chr> <int>
#  1 Chinua Achebe           Nigeria       images… English "htt…   209 Thin…  1958
#  2 Hans Christian Andersen Denmark       images… Danish  "htt…   784 Fair…  1836
#  3 Dante Alighieri         Italy         images… Italian "htt…   928 The …  1315
#  4 Unknown                 Sumer and Ak… images… Akkadi… "htt…   160 The … -1700
#  5 Unknown                 Achaemenid E… images… Hebrew  "htt…   176 The …  -600
#  6 Unknown                 India/Iran/I… images… Arabic  "htt…   288 One …  1200
#  7 Unknown                 Iceland       images… Old No… "htt…   384 Njál…  1350
#  8 Jane Austen             United Kingd… images… English "htt…   226 Prid…  1813
#  9 Honoré de Balzac        France        images… French  "htt…   443 Le P…  1835
# 10 Samuel Beckett          Republic of … images… French… "htt…   256 Moll…  1952
# … with more rows, and abbreviated variable names ¹​imageLink, ²​language
# ℹ Use `print(n = ...)` to see more rows

collect(jsonDF)

# A tibble: 100 × 8
#    author                  country       image…¹ langu…² link  pages title  year
#    <chr>                   <chr>         <chr>   <chr>   <chr> <int> <chr> <int>
#  1 Chinua Achebe           Nigeria       images… English "htt…   209 Thin…  1958
#  2 Hans Christian Andersen Denmark       images… Danish  "htt…   784 Fair…  1836
#  3 Dante Alighieri         Italy         images… Italian "htt…   928 The …  1315
#  4 Unknown                 Sumer and Ak… images… Akkadi… "htt…   160 The … -1700
#  5 Unknown                 Achaemenid E… images… Hebrew  "htt…   176 The …  -600
#  6 Unknown                 India/Iran/I… images… Arabic  "htt…   288 One …  1200
#  7 Unknown                 Iceland       images… Old No… "htt…   384 Njál…  1350
#  8 Jane Austen             United Kingd… images… English "htt…   226 Prid…  1813
#  9 Honoré de Balzac        France        images… French  "htt…   443 Le P…  1835
# 10 Samuel Beckett          Republic of … images… French… "htt…   256 Moll…  1952
# … with 90 more rows, and abbreviated variable names ¹​imageLink, ²​language
# ℹ Use `print(n = ...)` to see more rows

تشغيل استعلامات SQL، والكتابة إلى جدول والقراءة منه

يمكنك استخدام دالات dplyr لتشغيل استعلامات SQL على DataFrame. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لاستخدامها dplyr::group_by والحصول dployr::count على عدد حسب الكاتب من DataFrame المسمى jsonDF. استخدم dplyr::arrange و dplyr::desc لفرز النتيجة بترتيب تنازلي حسب العد. ثم اطبع أول 10 صفوف بشكل افتراضي.

group_by(jsonDF, author) %>%
  count() %>%
  arrange(desc(n))

# Source:     spark<?> [?? x 2]
# Ordered by: desc(n)
#    author                     n
#    <chr>                  <dbl>
#  1 Fyodor Dostoevsky          4
#  2 Unknown                    4
#  3 Leo Tolstoy                3
#  4 Franz Kafka                3
#  5 William Shakespeare        3
#  6 William Faulkner           2
#  7 Gustave Flaubert           2
#  8 Homer                      2
#  9 Gabriel García Márquez     2
# 10 Thomas Mann                2
# … with more rows
# ℹ Use `print(n = ...)` to see more rows

يمكنك بعد ذلك استخدام sparklyr::spark_write_table لكتابة النتيجة إلى جدول في Azure Databricks. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لإعادة تشغيل الاستعلام ثم كتابة النتيجة إلى جدول يسمى json_books_agg:

group_by(jsonDF, author) %>%
  count() %>%
  arrange(desc(n)) %>%
  spark_write_table(
    name = "json_books_agg",
    mode = "overwrite"
  )

للتحقق من إنشاء الجدول، يمكنك بعد ذلك استخدام sparklyr::sdf_sql مع SparkR::showDF لعرض بيانات الجدول. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات للاستعلام عن الجدول في DataFrame ثم استخدم sparklyr::collect لطباعة أول 10 صفوف من DataFrame بشكل افتراضي:

collect(sdf_sql(sc, "SELECT * FROM json_books_agg"))

# A tibble: 82 × 2
#    author                     n
#    <chr>                  <dbl>
#  1 Fyodor Dostoevsky          4
#  2 Unknown                    4
#  3 Leo Tolstoy                3
#  4 Franz Kafka                3
#  5 William Shakespeare        3
#  6 William Faulkner           2
#  7 Homer                      2
#  8 Gustave Flaubert           2
#  9 Gabriel García Márquez     2
# 10 Thomas Mann                2
# … with 72 more rows
# ℹ Use `print(n = ...)` to see more rows

يمكنك أيضا استخدام sparklyr::spark_read_table للقيام بشيء مشابه. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات للاستعلام عن DataFrame السابق المسمى jsonDF في DataFrame ثم استخدم sparklyr::collect لطباعة أول 10 صفوف من DataFrame بشكل افتراضي:

fromTable <- spark_read_table(
  sc   = sc,
  name = "json_books_agg"
)

collect(fromTable)

# A tibble: 82 × 2
#    author                     n
#    <chr>                  <dbl>
#  1 Fyodor Dostoevsky          4
#  2 Unknown                    4
#  3 Leo Tolstoy                3
#  4 Franz Kafka                3
#  5 William Shakespeare        3
#  6 William Faulkner           2
#  7 Homer                      2
#  8 Gustave Flaubert           2
#  9 Gabriel García Márquez     2
# 10 Thomas Mann                2
# … with 72 more rows
# ℹ Use `print(n = ...)` to see more rows

إضافة أعمدة واحسب قيم الأعمدة في DataFrame

يمكنك استخدام دالات dplyr لإضافة أعمدة إلى DataFrames وإلى حساب قيم الأعمدة.

على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات للحصول على محتويات DataFrame المسماة jsonDF. استخدم dplyr::mutate لإضافة عمود باسم today، واملأ هذا العمود الجديد بالطابع الزمني الحالي. ثم اكتب هذه المحتويات إلى DataFrame جديد يسمى withDate واستخدم dplyr::collect لطباعة أول 10 صفوف في DataFrame الجديدة بشكل افتراضي.

إشعار

dplyr::mutate يقبل فقط الوسيطات التي تتوافق مع الدالات المضمنة في Hive (المعروفة أيضا باسم UDFs) والدالات التجميعية المضمنة (المعروفة أيضا باسم UDAFs). للحصول على معلومات عامة، راجع وظائف Hive. للحصول على معلومات حول الدالات المتعلقة بالتاريخ في هذا القسم، راجع دالات التاريخ.

withDate <- jsonDF %>%
  mutate(today = current_timestamp())

collect(withDate)

# A tibble: 100 × 9
#    author    country image…¹ langu…² link  pages title  year today
#    <chr>     <chr>   <chr>   <chr>   <chr> <int> <chr> <int> <dttm>
#  1 Chinua A… Nigeria images… English "htt…   209 Thin…  1958 2022-09-27 21:32:59
#  2 Hans Chr… Denmark images… Danish  "htt…   784 Fair…  1836 2022-09-27 21:32:59
#  3 Dante Al… Italy   images… Italian "htt…   928 The …  1315 2022-09-27 21:32:59
#  4 Unknown   Sumer … images… Akkadi… "htt…   160 The … -1700 2022-09-27 21:32:59
#  5 Unknown   Achaem… images… Hebrew  "htt…   176 The …  -600 2022-09-27 21:32:59
#  6 Unknown   India/… images… Arabic  "htt…   288 One …  1200 2022-09-27 21:32:59
#  7 Unknown   Iceland images… Old No… "htt…   384 Njál…  1350 2022-09-27 21:32:59
#  8 Jane Aus… United… images… English "htt…   226 Prid…  1813 2022-09-27 21:32:59
#  9 Honoré d… France  images… French  "htt…   443 Le P…  1835 2022-09-27 21:32:59
# 10 Samuel B… Republ… images… French… "htt…   256 Moll…  1952 2022-09-27 21:32:59
# … with 90 more rows, and abbreviated variable names ¹​imageLink, ²​language
# ℹ Use `print(n = ...)` to see more rows

استخدم dplyr::mutate الآن لإضافة عمودين إضافيين إلى محتويات withDate DataFrame. تحتوي الأعمدة الجديدة month والشهر year الرقمي والسنة من today العمود. ثم اكتب هذه المحتويات إلى DataFrame جديد يسمى withMMyyyy، واستخدم dplyr::select مع dplyr::collect لطباعة monthauthortitleالأعمدة و و year من الصفوف العشرة الأولى من DataFrame الجديدة بشكل افتراضي:

withMMyyyy <- withDate %>%
  mutate(month = month(today),
         year  = year(today))

collect(select(withMMyyyy, c("author", "title", "month", "year")))

# A tibble: 100 × 4
#    author                  title                                     month  year
#    <chr>                   <chr>                                     <int> <int>
#  1 Chinua Achebe           Things Fall Apart                             9  2022
#  2 Hans Christian Andersen Fairy tales                                   9  2022
#  3 Dante Alighieri         The Divine Comedy                             9  2022
#  4 Unknown                 The Epic Of Gilgamesh                         9  2022
#  5 Unknown                 The Book Of Job                               9  2022
#  6 Unknown                 One Thousand and One Nights                   9  2022
#  7 Unknown                 Njál's Saga                                   9  2022
#  8 Jane Austen             Pride and Prejudice                           9  2022
#  9 Honoré de Balzac        Le Père Goriot                                9  2022
# 10 Samuel Beckett          Molloy, Malone Dies, The Unnamable, the …     9  2022
# … with 90 more rows
# ℹ Use `print(n = ...)` to see more rows

استخدم dplyr::mutate الآن لإضافة عمودين إضافيين إلى محتويات withMMyyyy DataFrame. تحتوي الأعمدة الجديدة formatted_date على الجزء من today العمود، بينما يحتوي العمود الجديد day على اليوم الرقمي من العمود الجديد formatted_dateyyyy-MM-dd. ثم اكتب هذه المحتويات إلى DataFrame جديد يسمى withUnixTimestamp، واستخدم dplyr::select مع dplyr::collect لطباعة titleformatted_dateالأعمدة و و day من الصفوف العشرة الأولى من DataFrame الجديدة بشكل افتراضي:

withUnixTimestamp <- withMMyyyy %>%
  mutate(formatted_date = date_format(today, "yyyy-MM-dd"),
         day            = dayofmonth(formatted_date))

collect(select(withUnixTimestamp, c("title", "formatted_date", "day")))

# A tibble: 100 × 3
#    title                                           formatted_date   day
#    <chr>                                           <chr>          <int>
#  1 Things Fall Apart                               2022-09-27        27
#  2 Fairy tales                                     2022-09-27        27
#  3 The Divine Comedy                               2022-09-27        27
#  4 The Epic Of Gilgamesh                           2022-09-27        27
#  5 The Book Of Job                                 2022-09-27        27
#  6 One Thousand and One Nights                     2022-09-27        27
#  7 Njál's Saga                                     2022-09-27        27
#  8 Pride and Prejudice                             2022-09-27        27
#  9 Le Père Goriot                                  2022-09-27        27
# 10 Molloy, Malone Dies, The Unnamable, the trilogy 2022-09-27        27
# … with 90 more rows
# ℹ Use `print(n = ...)` to see more rows

إنشاء طريقة عرض مؤقتة

يمكنك إنشاء طرق عرض مؤقتة مسماة في الذاكرة تستند إلى DataFrames الموجودة. على سبيل المثال، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لاستخدامها SparkR::createOrReplaceTempView للحصول على محتويات DataFrame السابقة المسماة jsonTable وإجراء طريقة عرض مؤقتة خارجها باسم timestampTable. ثم استخدم sparklyr::spark_read_table لقراءة محتويات طريقة العرض المؤقتة. استخدم sparklyr::collect لطباعة أول 10 صفوف من الجدول المؤقت بشكل افتراضي:

createOrReplaceTempView(withTimestampDF, viewName = "timestampTable")

spark_read_table(
  sc = sc,
  name = "timestampTable"
) %>% collect()

# A tibble: 100 × 10
#    author    country image…¹ langu…² link  pages title  year today
#    <chr>     <chr>   <chr>   <chr>   <chr> <int> <chr> <int> <dttm>
#  1 Chinua A… Nigeria images… English "htt…   209 Thin…  1958 2022-09-27 21:11:56
#  2 Hans Chr… Denmark images… Danish  "htt…   784 Fair…  1836 2022-09-27 21:11:56
#  3 Dante Al… Italy   images… Italian "htt…   928 The …  1315 2022-09-27 21:11:56
#  4 Unknown   Sumer … images… Akkadi… "htt…   160 The … -1700 2022-09-27 21:11:56
#  5 Unknown   Achaem… images… Hebrew  "htt…   176 The …  -600 2022-09-27 21:11:56
#  6 Unknown   India/… images… Arabic  "htt…   288 One …  1200 2022-09-27 21:11:56
#  7 Unknown   Iceland images… Old No… "htt…   384 Njál…  1350 2022-09-27 21:11:56
#  8 Jane Aus… United… images… English "htt…   226 Prid…  1813 2022-09-27 21:11:56
#  9 Honoré d… France  images… French  "htt…   443 Le P…  1835 2022-09-27 21:11:56
# 10 Samuel B… Republ… images… French… "htt…   256 Moll…  1952 2022-09-27 21:11:56
# … with 90 more rows, 1 more variable: month <chr>, and abbreviated variable
#   names ¹​imageLink, ²​language
# ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names

إجراء تحليل إحصائي على DataFrame

يمكنك استخدام sparklyr مع dplyr للتحليلات الإحصائية.

على سبيل المثال، قم بإنشاء DataFrame لتشغيل الإحصائيات عليه. للقيام بذلك، قم بتشغيل التعليمات البرمجية التالية في خلية دفتر ملاحظات لاستخدامها sparklyr::sdf_copy_to لكتابة iris محتويات مجموعة البيانات المضمنة في R إلى DataFrame المسمى iris. استخدم sparklyr::sdf_collect لطباعة أول 10 صفوف من الجدول المؤقت بشكل افتراضي:

irisDF <- sdf_copy_to(
  sc        = sc,
  x         = iris,
  name      = "iris",
  overwrite = TRUE
)

sdf_collect(irisDF, "row-wise")

# A tibble: 150 × 5
#    Sepal_Length Sepal_Width Petal_Length Petal_Width Species
#           <dbl>       <dbl>        <dbl>       <dbl> <chr>
#  1          5.1         3.5          1.4         0.2 setosa
#  2          4.9         3            1.4         0.2 setosa
#  3          4.7         3.2          1.3         0.2 setosa
#  4          4.6         3.1          1.5         0.2 setosa
#  5          5           3.6          1.4         0.2 setosa
#  6          5.4         3.9          1.7         0.4 setosa
#  7          4.6         3.4          1.4         0.3 setosa
#  8          5           3.4          1.5         0.2 setosa
#  9          4.4         2.9          1.4         0.2 setosa
# 10          4.9         3.1          1.5         0.1 setosa
# … with 140 more rows
# ℹ Use `print(n = ...)` to see more rows

استخدم dplyr::group_by الآن لتجميع الصفوف حسب Species العمود. استخدم dplyr::summarize مع dplyr::percentile_approx لحساب إحصائيات الملخص حسب الكمية 25 و50 و75 و100 للعمود Sepal_Length بواسطة Species. استخدم sparklyr::collect طباعة النتائج:

إشعار

dplyr::summarize يقبل فقط الوسيطات التي تتوافق مع الدالات المضمنة في Hive (المعروفة أيضا باسم UDFs) والدالات التجميعية المضمنة (المعروفة أيضا باسم UDAFs). للحصول على معلومات عامة، راجع وظائف Hive. للحصول على معلومات حول percentile_approx، راجع الدالات التجميعية المضمنة (UDAF).

quantileDF <- irisDF %>%
  group_by(Species) %>%
  summarize(
    quantile_25th = percentile_approx(
      Sepal_Length,
      0.25
    ),
    quantile_50th = percentile_approx(
      Sepal_Length,
      0.50
    ),
    quantile_75th = percentile_approx(
      Sepal_Length,
      0.75
    ),
    quantile_100th = percentile_approx(
      Sepal_Length,
      1.0
    )
  )

collect(quantileDF)

# A tibble: 3 × 5
#   Species    quantile_25th quantile_50th quantile_75th quantile_100th
#   <chr>              <dbl>         <dbl>         <dbl>          <dbl>
# 1 virginica            6.2           6.5           6.9            7.9
# 2 versicolor           5.6           5.9           6.3            7
# 3 setosa               4.8           5             5.2            5.8

يمكن حساب نتائج مماثلة، على سبيل المثال، باستخدام sparklyr::sdf_quantile:

print(sdf_quantile(
  x = irisDF %>%
    filter(Species == "virginica"),
  column = "Sepal_Length",
  probabilities = c(0.25, 0.5, 0.75, 1.0)
))

# 25%  50%  75% 100%
# 6.2  6.5  6.9  7.9