Nou în

Integrare Java

Acum puteți încorpora și executa cod Java direct în Stata. Anterior, puteai să creezi și să folosești pluginuri Java în Stata, dar asta îți impunea să îți compilezi codul și să-l grupezi într-un fișier JAR. Puteți face acest lucru în continuare, dar acum puteți scrie și codul dvs. Java direct în do-files, ado-files sau chiar interactiv. Codul Java pe care îl scrieți este compilat din mers – nu este necesar un compilator extern! Puteți chiar să puteți scrie cod paralelizat pentru a profita de mai multe nuclee.
Pachetul Java Stata Function Interface (sfi) inclus oferă o conexiune bidirecțională între Stata și Java.
Stata include pachetul de dezvoltare Java (JDK) cu instalarea sa, deci nu este necesară nicio configurare suplimentară. Această versiune a Stata include Java 11, care este versiunea actuală de suport pe termen lung (LTS).

Repere

  • Integrare Java cu Stata
    • Folosiți Java interactiv (cum ar fi JShell) din cadrul Stata
    • Încorporați codul Java în fișierele do
    • Încorporați codul Java în fișierele ado
    • Compilați codul Java din mers fără programe externe
  • Pachetul Java al Interfaței Funcției Stata (sfi)
    • Conexiune bidirecțională între Stata și Java
    • Accesați seturi de date Stata, cadre, macrocomenzi, scalare, matrici, etichete de valori, caracteristici, matrice Mata globale, valori de dată și oră și multe altele din Java

Să vedem cum funcționează

Exemplul 1: Invocați Java interactiv

Dacă sunteți familiarizat cu JShell, următoarele vor arăta asemănător cu dvs.

. java:
java (type end to exit and /help for help)
java> System.out.println("Hello, Java!"); Hello, Java! java>

În acest exemplu, am cerut Java să tipărească „Bună ziua, Java!” Putem apela Java interactiv din fereastra de comandă a Stata.

Exemplul 2: Încorporați codul Java într-un fișier do

Putem încorpora Java într-un fișier do, la fel cum facem cu codul Mata și Python. Plasați doar codul Java între java: și sfârșit.

--------------------- do-file (begin)----------------------------
java:
ArrayList<String> coffeeList = new ArrayList<>(Arrays.asList(
        "Latte", "Mocha", "Espresso", "Cappuccino"));

  Collections.sort(coffeeList);
  for ( String coffee : coffeeList ) {
        SFIToolkit.displayln(coffee) ;
  }
  end // leave Java
--------------------- do-file (end) -----------------------------

--------------------- output (begin) ----------------------------
. java:
java (type end to exit and /help for help)
java> ArrayList<String> coffeeList = new ArrayList<>(Arrays.asList( ...> "Latte", "Mocha", "Espresso", "Cappuccino")); coffeeList ==> [Latte, Mocha, Espresso, Cappuccino] java> java> Collections.sort(coffeeList); java> for ( String coffee : coffeeList ) { ...> SFIToolkit.displayln(coffee) ; ...> } Cappuccino Espresso Latte Mocha java> end // leave Java
--------------------- output (end) -------------------------------

Avem un ArrayList care conține patru băuturi de cafea diferite („Latte”, „Mocha”, „Espresso”, „Cappuccino”).
Am sortat ArrayList folosind Collections.sort () și apoi am imprimat lista sortată, câte una pe fiecare linie.
Cappuccino
Espresso
Latte
Mocha

Exemplul 3: Încorporați codul Java într-un fișier ado

Spre deosebire de Python, bibliotecile Java tind să aibă o implementare de nivel inferior, ceea ce înseamnă că este posibil să trebuiască să scrieți ceva mai mult cod pentru a face ceea ce doriți. Cu toate acestea, acest lucru oferă adesea o mai mare flexibilitate și performanță. Unul dintre punctele forte ale Java se află în API-urile sale extinse, care sunt împachetate cu mașina virtuală Java. Consultați setul de dezvoltare Java pentru detalii. Există, de asemenea, multe biblioteci utile de la terți disponibile.
Să presupunem că comanda egen a lui Stata nu avea deja o funcție de rowmean. Am putea folosi integrarea Java pentru a scrie o comandă Stata cu această funcționalitate.

--------- rowmean.ado (begin) ------------------------------------------------
program rowmean
    version 17
    syntax varlist [if] [in], GENerate(string)
    confirm new variable `generate'
    preserve
    quietly generate `generate' = .
    java `varlist' `if' `in' : Demo.rowmean("`generate'")
    restore, not
end

java:
import java.util.stream.LongStream;
public class Demo {
    public static void rowmean(String newvar) {
        long obs1 = Data.getObsParsedIn1(); // get observations for in range
        long obs2 = Data.getObsParsedIn2(); // get observations for in range
        int varCount = Data.getParsedVarCount();
        int idxStore = Data.getVarIndex(newvar);

        // loop over observations
        LongStream.rangeClosed(obs1, obs2).forEach(obs -> {
            double sum = 0;
            long count = 0;
            if (!Data.isParsedIfTrue(obs)) {
                return; // skip iteration of lambda expression
            }

            // loop over each variable
            for (int i = 1; i <= varCount; i++) {
                final int var = Data.mapParsedVarIndex(i);
                if (Data.isVarTypeString(var)) continue; // skip if string

                double value = Data.getNum(var, obs);
                if (Missing.isMissing(value)) continue;
                sum += value; count++;
            }
            Data.storeNumFast(idxStore, obs, sum/count);
        });
    }
}
end // end Java block
---------- rowmean.ado (end) -------------------------------------------------

Am pus codul nostru Java chiar în fișierul nostru ado. Se compilează din mers, pe măsură ce fișierul ado este încărcat și executat.
Să configurăm câteva exemple de date.

---------- setup-data.do (begin) ----------------------------
clear
set seed 12345
set obs 5000000           // create five million observations
forvalues i = 1/10 {      // generate ten variables
    gen v`i' = runiform()
}
---------- setup-data.do (end) -------------------------------

Să executăm comanda.

. rowmean v*, gen(rmean)

A durat 3,2 secunde. Nu e rău. O putem face mai repede? Pariem că putem! Putem profita de multiplele noastre procesoare. Aparatul pe care testez este un I7-5820K și are 6 nuclee CPU. O altă optimizare pe care o putem face este să nu cerem Stata în mod repetat pentru fiecare index de variabile. În schimb, putem obține aceste informații o dată și le putem ascunde în cache.

Exemplul 4: Faceți-o mai rapid

Iată comanda noastră îmbunătățită:

--------- rowmean.ado (begin) ------------------------------------------------
program rowmean
    version 17
    syntax varlist [if] [in], GENerate(string)
    confirm new variable `generate'
    preserve
    quietly generate `generate' = .
    java `varlist' `if' `in' : Demo.rowmean("`generate'")
    restore, not
end

java:
import java.util.stream.LongStream;
public class Demo {
    public static void rowmean(String newvar) {
        long obs1 = Data.getObsParsedIn1(); // get observations for in range
        long obs2 = Data.getObsParsedIn2(); // get observations for in range
        int varCount = Data.getParsedVarCount();
        int idxStore = Data.getVarIndex(newvar);

        // cache the variable indexes
        ArrayList<Integer> varmap = new ArrayList<Integer>();
        for (int i = 1; i <= varCount; i++) {
            int idx = Data.mapParsedVarIndex(i);
            if (!Data.isVarTypeString(idx)) {
                 varmap.add(idx);
            }
        }

        // loop over observations
        LongStream.rangeClosed(obs1, obs2).parallel().forEach(obs -> {
            double sum = 0;
            long count = 0;
            if (!Data.isParsedIfTrue(obs)) {
                return; // skip iteration of lambda expression
            }

            // loop over each variable
            for (int var : varmap) {
                double value = Data.getNum(var, obs);
                if (Missing.isMissing(value)) continue;
                sum += value; count++;
            }
            Data.storeNumFast(idxStore, obs, sum/count);
        });
    }
}
end // end Java block
---------- rowmean.ado (end) -------------------------------------------------

A făcut-o mai repede? Sigur a făcut-o! Momentul nostru inițial a fost de 3,2 secunde. De data aceasta, utilizând același set de date, comanda s-a încheiat în .79 secunde. Cea mai mare parte a îmbunătățirii a fost ca codul nostru să ruleze în paralel. Dacă nu priviți cu atenție, este posibil să nu observați schimbarea pe care am făcut-o. Deoarece folosim „LongStream” pentru a trece peste observații, putem cere Java să ruleze asta în paralel invocând metoda parallel () înainte de foreach () în expresia lambda. Rețineți că scrierea codului paralel are multe capcane și poate să nu fie ușor pentru probleme mai complicate. Și pentru alte probleme, paralelizarea poate să nu fie benefică.