1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

bamboolib | Databricks on AWS [2022/7/8時点]の翻訳です。

本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。

プレビュー
本機能はパブリックプレビューです。

注意
bamboolibはDatabricksランタイム11.0以降でサポートされています。

bamboolibは、Databricksノートブックでノーコードのデータ分析やデータ変換を可能とするユーザーインタフェースのコンポーネントです。bamboolibを用いることで、ユーザーはより容易にデータを取り扱うことができるようになり、一般的なデータ操作、データ探索、可視化のタスクをスピードアップすることができます。ユーザーがデータに対してこれらのタスクを完了すると、bamboolibはバックグラウンドで自動でPythonコードを生成します。ユーザーはこのコードを他のユーザーに共有することができ、クイックに自分のノートブックでオリジナルのタスクを再現することができます。また、ここでもbamboolibを用いることで、どのようにコーディングするのかを知ることなしに、これらのオリジナルのタスクを拡張することができます。コーディングの経験のある人は、より洗練された結果を得るためにこのコードを拡張することができます。

内部では、bamboolibはIPythonカーネル向けのインタラクティブなHTMLウィジェットフレームワークであるipywidgetsを使用しています。ipywidgetsはIPythonカーネル内で実行されます。

要件

クイックスタート

  1. Pythonノートブックを作成します。

  2. 要件を満たすクラスターにノートブックをアタッチします。

  3. ノートブックの最初のセルに以下のコードを入力し、セルを実行します。

    Python
    import bamboolib as bam
    
  4. ノートブックの2つ目のセルに以下のコードを入力し、セルを実行します。

    Python
    bam
    

    注意
    あるいは、特定のデータフレームを使用するために既存のpandasデータフレームを表示することができます。

  5. キータスクを行います。

ウォークスルー

bamboolib自身、あるいは既存のpandasデータフレームを用いてbamboolibを使用することができます。

bamboolib自身を使用する

このウォークスルーでは、サンプルのセールスデータセットのコンテンツをノートブックに表示するためにbamboolibを使用します。

  1. Pythonノートブックを作成します。

  2. 要件を満たすクラスターにノートブックをアタッチします。

  3. ノートブックの最初のセルに以下のコードを入力し、セルを実行します。

    Python
    import bamboolib as bam
    
  4. ノートブックの2つ目のセルに以下のコードを入力し、セルを実行します。

    Python
    bam
    
  5. Load dummy dataをクリックします。

  6. Load dummy dataペインのLoad a dummy data set for testing bamboolibからSales datasetを選択します。

  7. Executeをクリックします。

  8. item_typeBaby Foodである行を全て表示します。

    1. Search actionsリストでFilter rowsを選択します。
    2. Filter rowsペインのChooseリスト(whereの上)からSelect rowsを選択します。
    3. whereの下のリストからitem_typeを選択します。
    4. item_typeの隣のChooseリストで、has value(s) を選択します。
    5. has value(s) の隣のChoose value(s) ボックスでBaby Foodを選択します。
    6. Executeをクリックします。
  9. このクエリー用に自動で生成されたPythonコードをコピーします。

    1. Get Codeをクリックします。
    2. Export codeペインでCopy codeをクリックします。
  10. コードを貼り付けて修正します。

    1. ノートブックの3つ目のセルに、コピーしたコードを貼り付けます。以下のようになります。
    Python
    import pandas as pd
    df = pd.read_csv(bam.sales_csv)
    # Step: Keep rows where item_type is one of: Baby Food
    df = df.loc[df['item_type'].isin(['Baby Food'])]
    
    1. order_prioCである行のみを表示するようにコードを追加し、セルを実行します。
    Python
    import pandas as pd
    df = pd.read_csv(bam.sales_csv)
    # Step: Keep rows where item_type is one of: Baby Food
    df = df.loc[df['item_type'].isin(['Baby Food'])]
    
    # Add the following code.
    # Step: Keep rows where order_prio is one of: C
    df = df.loc[df['order_prio'].isin(['C'])]
    df
    

    ティップス
    order_prioCである行のみを表示するようにするために、このコードを記述する代わりに2番目のセルでbamboolibを使用することもできます。このステップは、前のステップでbamboolibが自動で生成したコードを拡張するサンプルとなっています。

  11. regionの昇順で行をソートします。

    1. 3つ目のセルのウィジェットのSearch actionsリストでSort rowsを選択します。
    2. Sort column(s)ペインのChoose columnリストでregionを選択します。
    3. regionの隣でascending (A-Z) を選択します。
    4. Executeをクリックします。

    注意
    これは以下のコードを記述するのと等価です。

    Python
    df = df.sort_values(by=['region'], ascending=[True])
    df
    

    regionの昇順で行をソートするために2つ目のセルでbamboolibを使用することもできます。このステップは、前のステップでbamboolibが自動で生成したコードを拡張するサンプルとなっています。bamboolibを使うと、追加のコードが自動で生成されるので、さらに自動で拡張されたコードを自分の手で拡張することもできます!

  12. キータスクを行います。

既存のデータフレームでbamboolibを使用する

  1. Pythonノートブックを作成します。

  2. 要件を満たすクラスターにノートブックをアタッチします。

  3. ノートブックの最初のセルに以下のコードを入力し、セルを実行します。

    Python
    import bamboolib as bam
    
  4. ノートブックの2つ目のセルで、以下のコードを入力しセルを実行します。

    Python
    import pandas as pd
    
    df = pd.read_csv(bam.sales_csv)
    df
    

    bamboolibはpandasデータフレームのみをサポートしていることに注意してください。PySparkデータフレームをpandasデータフレームに変換するには、PySparkデータフレームに対してtoPandasを呼び出します。Pandas API on Sparkデータフレームをpandasデータフレームに変換するには、Pandas API on Sparkデータフレームに対してto_pandasを呼び出します。

  5. Show bamboolib UIをクリックします。

  6. item_typeBaby Foodである行を全て表示します。

    1. Search actionsリストでFilter rowsを選択します。
    2. Filter rowsペインのChooseリスト(whereの上)からSelect rowsを選択します。
    3. whereの下のリストからitem_typeを選択します。
    4. item_typeの隣のChooseリストで、has value(s) を選択します。
    5. has value(s) の隣のChoose value(s) ボックスでBaby Foodを選択します。
    6. Executeをクリックします。
  7. このクエリー用に自動で生成されたPythonコードをコピーします。

    1. Get Codeをクリックします。
    2. Export codeペインでCopy codeをクリックします。
  8. コードを貼り付けて修正します。

    1. ノートブックの3つ目のセルに、コピーしたコードを貼り付けます。以下のようになります。
    Python
    import pandas as pd
    df = pd.read_csv(bam.sales_csv)
    # Step: Keep rows where item_type is one of: Baby Food
    df = df.loc[df['item_type'].isin(['Baby Food'])]
    
    1. order_prioCである行のみを表示するようにコードを追加し、セルを実行します。
    Python
    import pandas as pd
    df = pd.read_csv(bam.sales_csv)
    # Step: Keep rows where item_type is one of: Baby Food
    df = df.loc[df['item_type'].isin(['Baby Food'])]
    
    # Add the following code.
    # Step: Keep rows where order_prio is one of: C
    df = df.loc[df['order_prio'].isin(['C'])]
    df
    

    ティップス
    order_prioCである行のみを表示するようにするために、このコードを記述する代わりに2番目のセルでbamboolibを使用することもできます。このステップは、前のステップでbamboolibが自動で生成したコードを拡張するサンプルとなっています。

  9. regionの昇順で行をソートします。

    1. 3つ目のセルのウィジェットのSearch actionsリストでSort rowsを選択します。
    2. Sort column(s)ペインのChoose columnリストでregionを選択します。
    3. regionの隣でascending (A-Z) を選択します。
    4. Executeをクリックします。

    注意
    これは以下のコードを記述するのと等価です。

    Python
    df = df.sort_values(by=['region'], ascending=[True])
    df
    

    regionの昇順で行をソートするために2つ目のセルでbamboolibを使用することもできます。このステップは、前のステップでbamboolibが自動で生成したコードを拡張するサンプルとなっています。bamboolibを使うと、追加のコードが自動で生成されるので、さらに自動で拡張されたコードを自分の手で拡張することもできます!

  10. キータスクを行います。

キータスク

セルにウィジェットを追加する

シナリオ: セルにbamboolibウィジェットを表示したいと考えています。

  1. ノートブックがbamboolibの要件を満たしていることを確認します。

  2. ノートブックで以下のコードを実行します。最初のセルで実行することが望ましいです。

    Python
    import bamboolib as bam
    
  3. オプション1: ウィジェットを表示したいセルで以下のコードを追加し、セルを実行します。

    Python
    bam
    

    コードの下にウィジェットが表示されます。

    あるいは:

    オプション2: pandasデータフレームへのリファレンスを含むセルでデータフレームを表示します。例えば、以下のデータフレーム定義を用いてセルを実行します。

    Python
    import pandas as pd
    from datetime import datetime, date
    
    df = pd.DataFrame({
      'a': [ 1, 2, 3 ],
      'b': [ 2., 3., 4. ],
      'c': [ 'string1', 'string2', 'string3' ],
      'd': [ date(2000, 1, 1), date(2000, 2, 1), date(2000, 3, 1) ],
      'e': [ datetime(2000, 1, 1, 12, 0), datetime(2000, 1, 2, 12, 0), datetime(2000, 1, 3, 12, 0) ]
    })
    
    df
    

    セルの下にウィジェットが表示されます。

    bamboolibはpandasデータフレームのみをサポートしていることに注意してください。PySparkデータフレームをpandasデータフレームに変換するには、PySparkデータフレームに対してtoPandasを呼び出します。Pandas API on Sparkデータフレームをpandasデータフレームに変換するには、Pandas API on Sparkデータフレームに対してto_pandasを呼び出します。

ウィジェットをクリアする

シナリオ: ウィジェットのコンテンツをクリアし、既存のウィジェットに新たなデータを読み込みたいと考えています。

オプション1: ターゲットのウィジェットを含むセルで以下のコードを実行します。

Python
bam

ウィジェットがクリアされ、Databricks: Read CSV file from DBFSDatabricks: Load database tableLoad dummy dataボタンが表示されます。

注意
name 'bam' is not definedというエラーが表示される場合、ノートブック(最初のセルが望ましいです)で以下のコマンドを実行し、再度トライしてください。

Python
import bamboolib as bam

オプション2: pandasデータフレームへのリファレンスを含むセルで、セルを再実行することでデータフレームを再度表示します。ウィジェットがクリアされ、新たなデータが表示されます。

データロードのタスク

サンプルデータセットのコンテンツをウィジェットに読み込む

シナリオ: ウィジェットにサンプルデータを読み込みたいものとします。例えば、ウィジェットの機能を試すためにダミーのセールスデータを読み込みます。

  1. Load dummy dataをクリックします。

  2. Load dummy dataペインのLoad a dummy data set for testing bamboolibで、ロードしたいデータセットの名前を選択します。

  3. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。

  4. Executeをクリックします。

    ウィジェットにデータセットのコンテンツが表示されます。

ティップス
現在のウィジェットの表示を別のサンプルデータセットのコンテンツに切り替えることができます。

  1. 現在のウィジェットでLoad dummy dataタブをクリックします。
  2. ウィジェットに別のサンプルデータセットのコンテンツを読み込むために上のステップを踏みます。

CSVファイルのコンテンツをウィジェットに読み込む

シナリオ: DatabricksワークスペースにあるCSVファイルのコンテンツをウィジェットに読み込みたいものとします。

  1. Databricks: Read CSV file from DBFSをクリックします。

  2. Read CSV from DBFSペインでターゲットのCSVファイルを含む格納場所をブラウジングします。

  3. ターゲットのCSVファイルを選択します。

  4. Dataframe nameには、データフレームとしてCSVファイルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。

  5. CSV value separatorでは、CSVファイルの区切り文字を入力するか、デフォルトの区切り文字の,(カンマ)のままとします。

  6. Decimal separatorでは、CSVファイルの数値の区切り文字を入力するかデフォルトの.(ドット)のままとします。

  7. Row limit: read the first N rows - leave empty for no limitでは、ウィジェットに読み込む最大行数を指定するか、あるいはデフォルト値の100000のままにするか、制限を指定しない場合には空にします。

  8. Open CSV fileをクリックします。

    指定された設定に基づき、ウィジェットにCSVファイルのコンテンツが表示されます。

ティップス
現在のウィジェットの表示を別のCSVファイルのコンテンツに切り替えることができます。

  1. 現在のウィジェットでRead CSV from DBFSタブをクリックします。
  2. ウィジェットに別のCSVファイルのコンテンツを読み込むために上のステップを踏みます。

データベーステーブルのコンテンツをウィジェットに読み込む

シナリオ: Databricksワークスペースにあるデータベースのテーブルのコンテンツをウィジェットに読み込みたいものとします。

  1. Databricks: Load database tableをクリックします。

    Databricks: Load database tableが表示されない場合、ウィジェットをクリアする - オプション1を試してみてください。

  2. Databricks: Load database tableペインのDatabase - leave empty for default databaseでターゲットテーブルが存在するデータベース名を入力するか、defaultデータベースを選択するためにボックスを空のままにしておきます。

  3. Tableには、ターゲットテーブル名を入力します。

  4. Row limit: read the first N rows - leave empty for no limitでは、ウィジェットに読み込む最大行数を指定するか、あるいはデフォルト値の100000のままにするか、制限を指定しない場合には空にします。

  5. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。

  6. Executeをクリックします。

    指定された設定に基づき、ウィジェットにテーブルのコンテンツが表示されます。

ティップス
現在のウィジェットの表示を別のテーブルのコンテンツに切り替えることができます。

  1. 現在のウィジェットでDatabricks: Load database tableタブをクリックします。
  2. ウィジェットに別のテーブルのコンテンツを読み込むために上のステップを踏みます。

データアクションのタスク

bamboolibでは50以上のデータアクションを提供しています。以下では、最初に使い始める一般的なデータアクションタスクを説明します。

カラムの選択

シナリオ: 特定の名前のカラム、特定のデータタイプのカラム、あるいは正規表現にマッチするカラムのみを表示したいものとします。例えば、ダミーのSales datasetにおいて、item_typesales_channelカラムのみを表示したい、あるいは、カラム名に文字列_dateを含むカラムのみを表示したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. selectと入力し、Select or drop columnsを選択します。
    2. Select or drop columnsを選択します。
  2. Select or drop columnsペインChooseドロップダウンリストで、Selectを選択します。
  3. ターゲットのカラム名あるいは条件を選択します。
  4. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  5. Executeをクリックします。

カラムの削除

シナリオ: 特定の名前のカラム、特定のデータタイプのカラム、あるいは正規表現にマッチするカラムを非表示にしたいものとします。例えば、ダミーのSales datasetにおいて、order_prioorder_dateship_dateカラムを非表示にしたい、あるいは、date-timeの値のみを持つすべてのカラムを非表示にしたいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. selectと入力し、Select or drop columnsを選択します。
    2. Select or drop columnsを選択します。
  2. Select or drop columnsペインChooseドロップダウンリストで、Dropを選択します。
  3. ターゲットのカラム名あるいは条件を選択します。
  4. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  5. Executeをクリックします。

行のフィルタリング

シナリオ: 特定のカラムの値がマッチする、あるいは欠損しているといった評価指標に基づいて特定のテーブルの行を表示、非表示したいものとします。例えば、ダミーのSales datasetで、item_typeカラムの値がBaby Foodである行のみを表示したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. filterと入力し、Filter rowsを選択します。
    2. Filter rowsを選択します。
  2. Filter rowsペインのwhereの上にあるChooseドロップダウンリストで、Select rowsDrop rowsを選択します。
  3. 最初のフィルタリング条件を指定します。
  4. 別のフィルタリング条件を追加するには、add conditionをクリックし、次のフィルタリング条件を指定します。必要なだけこの操作を繰り返します。
  5. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  6. Executeをクリックします。

行のソート

シナリオ: 1つ以上のカラムの値に基づいてテーブルの行をソートしたいものとします。例えば、ダミーのSales datasetregionカラムのアルファベット順で行をソートしたいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. sortと入力し、Sort rowsを選択します。
    2. Sort rowsを選択します。
  2. Sort column(s) ペインでソートする最初のカラムとソート順を選択します。
  3. 別のソート条件を追加するには、add columnをクリックし、次のソート条件を指定します。必要なだけこの操作を繰り返します。
  4. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  5. Executeをクリックします。

行と列のグルーピングのタスク

単一の集計関数による行と列のグルーピング

シナリオ: グループの計算結果による行と列の結果を表示し、これらのグルーピングに対してカスタムの名称を割り当てたいものとします。例えば、ダミーのSales datasetでカラムcountryの値に基づいて行をグルーピングし、同じcountryの値を持つ行の数を表示し、計算されたカウントの名前をcountry_countにしたいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. groupと入力し、Group by and aggregate (with renaming) を選択します。
    2. Group by and aggregate (with renaming) を選択します。
  2. Group by with column renameペインでグルーピングするカラムを選択し、計算処理、そしてオプションで計算値のカラム名を指定します。
  3. 別の計算処理を追加するには、add calculationをクリックし、次の計算処理とカラム名を指定します。必要なだけこの操作を繰り返します。
  4. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  5. Executeをクリックします。

複数の集計関数による行と列のグルーピング

シナリオ: 計算されたグループの結果の行と列を表示したいものとします。例えば、ダミーのSales datasetregioncountrysales_channelの値に基づいてグルーピングを行い、sales_channelごとに同じregioncountryを持つ行の数、そして、regioncountrysales_channelのユニークな組み合わせによるtotal_revenueを表示したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. groupと入力し、Group by and aggregate (default) を選択します。
    2. Group by and aggregate (default) を選択します。
  2. Group by with column renameペインでグルーピングするカラムを選択し、計算処理、そしてオプションで計算値のカラム名を指定します。
  3. 別の計算処理を追加するには、add calculationをクリックし、次の計算処理とカラム名を指定します。必要なだけこの操作を繰り返します。
  4. 結果をどこに格納するのかを指定します。
  5. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  6. Executeをクリックします。

欠損値を持つ行の削除

シナリオ: 特定のカラムで欠損値を持つ行を削除したいものとします。例えば、ダミーのSales datasetで、item_typeに値のない行を削除したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. dropあるいはremoveと入力し、Drop missing valuesを選択します。
    2. Drop missing valuesを選択します。
  2. Drop missing valuesペインで欠損値をチェックするカラムを指定します。
  3. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  4. Executeをクリックします。

重複行の削除

シナリオ: 特定カラムで重複値がある行を削除したいものとします。例えば、ダミーのSales datasetで、全く同じ値を持つ行を削除したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. dropあるいはremoveと入力し、Drop/Remove duplicatesを選択します。
    2. Drop/Remove duplicatesを選択します。
  2. Drop Duplicatesペインで重複を確認するカラムを指定し、重複値を持つ最初の行、最後の行を保持するのかを選択します。
  3. Dataframe nameには、データフレームとしてテーブルのコンテンツのプログラム上の識別子を指定します。あるいは、デフォルトのdfのままとします。
  4. Executeをクリックします。

欠損値の検索・置換

シナリオ: 特定カラムで欠損値がある場合に何かしらの値で置換を行いたいものとします。例えば、ダミーのSales datasetitem_typeカラムの欠損値をUnknown Item Typeで置き換えたいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. findあるいはreplaceと入力し、Find and replace missing valuesを選択します。
    2. Find and replace missing valuesを選択します。
  2. Replace missing valuesペインで欠損値を置き換えるカラムと、置換する値を指定します。
  3. Executeをクリックします。

カラム数式の作成

シナリオ: ユニークな数式を使用するカラムを作成したいものとします。例えば、ダミーのSales datasetで各行のtotal_profitの値をunits_soldで割った結果をprofit_per_unitというカラムとして表示したいものとします。

  1. DataタブのSearch actionsドロップダウンリストで以下のいずれかを行います。
    1. formulaと入力し、New column formulaを選択します。
    2. New column formulaを選択します。
  2. Create new column from formulaペインで数式とカラム名を指定します。
  3. Executeをクリックします。

データアクション履歴のタスク

ウィジェットで実行済みアクションの一覧を参照する

シナリオ: ウィジェットで実行されたすべての変更を最初から最後まで参照したいものとします。

Historyをクリックします。Transformations historyペインにアクションの一覧が表示されます。

ウィジェットで最新のアクションを取り消す

シナリオ: ウィジェットで実行された最後のアクションを取り消したいものとします。

以下のいずれかを行います。

  • 逆時計回り矢印のアイコンをクリックします。
  • Historyをクリックします。Transformations historyペインでUndo last stepをクリックします。

ウィジェットで最新のアクションを再度実行する

シナリオ: 最新のアクションを取り消した後に、再度最新のアクションを実行したいものとします。

以下のいずれかを行います。

  • 時計回り矢印のアイコンをクリックします。
  • Historyをクリックします。Transformations historyペインでRecover last stepをクリックします。

ウィジェットで最新のアクションを変更する

シナリオ: ウィジェット常の最新のアクションを変更したいものとします。

  1. 以下のいずれかを行います。
    1. 鉛筆アイコンをクリックします。
    2. Historyをクリックします。Transformations historyペインでEdit last stepをクリックします。
  2. 必要な変更を行い、Executeをクリックします。

ウィジェットの現在の状態をプログラムでデータフレームとして再現するためのコードを取得する

シナリオ: 現在のウィジェットの状態をプログラム的に再現するPythonコードを取得し、pandasデータフレームとして表現したいと考えています。この
ノートブックの別のセル、あるいは別のノートブックでこのコードを実行することができます。

  1. Get Codeをクリックします。

  2. Export codeペインでCopy codeをクリックします。コードがクリップボードにコピーされます。

  3. ノートブックの別のセル、あるいは別のノートブックにコードを貼り付けます。

  4. プログラムからpandasデータフレームを操作するために追加のコードを記述し、セルを実行します。例えば、データフレームdfの中身を表示するために以下のコードを追加します。

    Python
    # Your pasted code here, followed by...
    df
    

その他のリソース

Databricks 無料トライアル

Databricks 無料トライアル

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?