From ea103cbb5fcaa64a2e77aef54abb65d2a59a5fb4 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 1 Jan 2024 16:53:42 +0000 Subject: [PATCH 01/43] Fixed Importer bug with 2.0 databases. Worked in 3.0.9 but then broke because version check was introduced in 3.0.10 to prevent old versions from being opened. As an unintended side-effect, it also prevented 2.0 databases from being opened in the batch importer. --- dna/src/main/java/dna/Dna.java | 2 +- dna/src/main/java/dna/HeadlessDna.java | 21 ++++-- dna/src/main/java/gui/MainWindow.java | 34 +++++---- dna/src/main/java/gui/NewDatabaseDialog.java | 2 - dna/src/main/java/sql/Sql.java | 77 ++++---------------- 5 files changed, 50 insertions(+), 86 deletions(-) diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 4caac62a..5dc60244 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2023-10-21"; + public static final String date = "2024-01-01"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/dna/src/main/java/dna/HeadlessDna.java b/dna/src/main/java/dna/HeadlessDna.java index f3ac8e06..93d6b37b 100644 --- a/dna/src/main/java/dna/HeadlessDna.java +++ b/dna/src/main/java/dna/HeadlessDna.java @@ -110,13 +110,22 @@ public boolean openDatabase(int coderId, String coderPassword, String type, Stri ConnectionProfile cp = new ConnectionProfile(type, databaseUrl, databaseName, databasePort, databaseUser, databasePassword); Sql testSql = new Sql(cp, true); boolean success = testSql.authenticate(coderId, coderPassword); + String version = testSql.getVersion(); if (success) { - Dna.sql.setConnectionProfile(cp, false); - Dna.sql.selectCoder(coderId); - LogEvent l = new LogEvent(Logger.MESSAGE, - "Coder " + Dna.sql.getActiveCoder().getId() + " (" + Dna.sql.getActiveCoder().getName() + ") successfully authenticated.", - "Coder " + Dna.sql.getActiveCoder().getId() + " (" + Dna.sql.getActiveCoder().getName() + ") successfully authenticated. You can now use the functions available to this user."); - Dna.logger.log(l); + if (version.startsWith("3.0")) { + Dna.sql.setConnectionProfile(cp, false); + Dna.sql.selectCoder(coderId); + LogEvent l = new LogEvent(Logger.MESSAGE, + "Coder " + Dna.sql.getActiveCoder().getId() + " (" + Dna.sql.getActiveCoder().getName() + ") successfully authenticated.", + "Coder " + Dna.sql.getActiveCoder().getId() + " (" + Dna.sql.getActiveCoder().getName() + ") successfully authenticated. You can now use the functions available to this user."); + Dna.logger.log(l); + } else { + LogEvent l = new LogEvent(Logger.ERROR, + "Tried to open an incompatible database version.", + "You tried to open a DNA database with version \" + version + \", but you can only open databases with version 3.0. Data from version 2 databases can also be imported into a new or existing DNA 3 database using the importer in the Documents menu."); + Dna.logger.log(l); + } + printDatabaseDetails(); } else { LogEvent l = new LogEvent(Logger.ERROR, diff --git a/dna/src/main/java/gui/MainWindow.java b/dna/src/main/java/gui/MainWindow.java index 89d384c7..1286ea51 100644 --- a/dna/src/main/java/gui/MainWindow.java +++ b/dna/src/main/java/gui/MainWindow.java @@ -1534,7 +1534,13 @@ public ActionOpenDatabase(String text, ImageIcon icon, String desc, Integer mnem public void actionPerformed(ActionEvent e) { NewDatabaseDialog n = new NewDatabaseDialog(MainWindow.this, true); ConnectionProfile cp = n.getConnectionProfile(); - if (cp != null) { + String version = new Sql(cp, false).getVersion(); + if (!version.startsWith("3.0")) { + LogEvent le = new LogEvent(Logger.ERROR, + "[GUI] Tried to open an incompatible database version.", + "You tried to open a DNA database with version " + version + ", but you can only open databases with version 3.0. Data from version 2 databases can also be imported into a new or existing DNA 3 database using the importer in the Documents menu."); + Dna.logger.log(le); + } else if (cp != null) { Dna.sql.setConnectionProfile(cp, false); refreshDocumentTable(); refreshStatementTable(new int[0]); @@ -1547,19 +1553,19 @@ public void actionPerformed(ActionEvent e) { statementPanel.adjustToChangedConnection(); menuBar.adjustToChangedCoder(); coderSelectionPanel.changeCoderBadge(); - } - - if (cp == null) { - LogEvent l = new LogEvent(Logger.MESSAGE, - "[GUI] Action executed: could not open database.", - "Started opening a database connection from the GUI, but the connection was not established."); - Dna.logger.log(l); - } else { - Dna.sql.setConnectionProfile(cp, false); // not a connection test, so false - LogEvent l = new LogEvent(Logger.MESSAGE, - "[GUI] Action executed: opened database.", - "Opened a database connection from the GUI."); - Dna.logger.log(l); + + if (cp == null) { + LogEvent l = new LogEvent(Logger.MESSAGE, + "[GUI] Action executed: could not open database.", + "Started opening a database connection from the GUI, but the connection was not established."); + Dna.logger.log(l); + } else { + Dna.sql.setConnectionProfile(cp, false); // not a connection test, so false + LogEvent l = new LogEvent(Logger.MESSAGE, + "[GUI] Action executed: opened database.", + "Opened a database connection from the GUI."); + Dna.logger.log(l); + } } } } diff --git a/dna/src/main/java/gui/NewDatabaseDialog.java b/dna/src/main/java/gui/NewDatabaseDialog.java index feec5a91..ea19f83d 100644 --- a/dna/src/main/java/gui/NewDatabaseDialog.java +++ b/dna/src/main/java/gui/NewDatabaseDialog.java @@ -19,7 +19,6 @@ import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; @@ -34,7 +33,6 @@ import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import javax.swing.filechooser.FileFilter; import org.jasypt.util.password.StrongPasswordEncryptor; diff --git a/dna/src/main/java/sql/Sql.java b/dna/src/main/java/sql/Sql.java index 69cd4bcd..48dcccb7 100644 --- a/dna/src/main/java/sql/Sql.java +++ b/dna/src/main/java/sql/Sql.java @@ -118,14 +118,12 @@ public boolean setConnectionProfile(ConnectionProfile cp, boolean test) { SQLiteDataSource sqds = new SQLiteDataSource(); sqds.setUrl("jdbc:sqlite:" + cp.getUrl()); sqds.setEnforceForeignKeys(true); // if this is not set, ON DELETE CASCADE won't work - if (checkDatabaseVersion(sqds)) { - ds = sqds; - success = true; - LogEvent l = new LogEvent(Logger.MESSAGE, - "[SQL] An SQLite DNA database has been opened as a data source.", - "An SQLite DNA database has been opened as a data source."); - Dna.logger.log(l); - } + ds = sqds; + success = true; + LogEvent l = new LogEvent(Logger.MESSAGE, + "[SQL] An SQLite DNA database has been opened as a data source.", + "An SQLite DNA database has been opened as a data source."); + Dna.logger.log(l); } else if (cp.getType().equals("mysql") || cp.getType().equals("postgresql")) { HikariConfig config = new HikariConfig(); config.setMaximumPoolSize(30); @@ -140,18 +138,16 @@ public boolean setConnectionProfile(ConnectionProfile cp, boolean test) { } try { HikariDataSource dsTest = new HikariDataSource(config); - if (checkDatabaseVersion(dsTest)) { - ds = dsTest; - success = true; - LogEvent l = new LogEvent(Logger.MESSAGE, - "[SQL] A " + cp.getType() + " DNA database has been opened as a data source.", - "A " + cp.getType() + " DNA database has been opened as a data source."); - Dna.logger.log(l); - } + ds = dsTest; + success = true; + LogEvent l = new LogEvent(Logger.MESSAGE, + "[SQL] A " + cp.getType() + " DNA database has been opened as a data source.", + "A " + cp.getType() + " DNA database has been opened as a data source."); + Dna.logger.log(l); } catch (PoolInitializationException e) { LogEvent l = new LogEvent(Logger.ERROR, - "[SQL] Database access denied. Failed to initialize connection pool.", - "Database access denied. Failed to initialize connection pool.", + "[SQL] Database access denied. Failed to initialize connection pool.", + "Database access denied. Failed to initialize connection pool.", e); Dna.logger.log(l); } @@ -178,51 +174,6 @@ public boolean hasDataSource() { return this.ds != null; } - /** - * Check if a data source is compatible with the current DNA version by inspecting the version number saved in the - * SETTINGS table. - * - * @param dataSource The data source to be used and checked for compatibility. - * @return True if the database version is compatible with the current DNA version. False if it needs to be updated. - */ - private boolean checkDatabaseVersion(DataSource dataSource) { - boolean compatible = true; - try (Connection conn = dataSource.getConnection(); - PreparedStatement s1 = conn.prepareStatement("SELECT * FROM SETTINGS WHERE Property IN ('version', 'date');")) { - ResultSet rs = s1.executeQuery(); - String property, version = "", date = ""; - while (rs.next()) { - property = rs.getString("Property"); - if (property.equals("version")) { - version = rs.getString("Value"); - } else if (property.equals("date")) { - date = rs.getString("Value"); - } - } - if (version.startsWith("1") || version.startsWith("2")) { - compatible = false; - String msg = ""; - if (version.startsWith("1")) { - msg = "Contents from databases that were created with DNA 1 can only be imported into the old DNA 2. See the release page online for old DNA 2 versions."; - - } else if (version.startsWith("2")) { - msg = "Contents from databases that were created with DNA 2 can be imported into the current DNA version. To do so, create a new database, create coders that correspond to the coders in the old database (if required), and use the \"Import from DNA database\" dialog in the \"Documents\" menu."; - } - LogEvent l = new LogEvent(Logger.ERROR, - "[SQL] Wrong database version.", - "You tried to open a database that was created with version " + version + " of DNA (release date: " + date + "). You are currently using DNA " + Dna.version + " (release date: " + Dna.date + "). The database version is incompatible with the DNA version. " + msg); - Dna.logger.log(l); - } - } catch (SQLException e) { - LogEvent l = new LogEvent(Logger.WARNING, - "[SQL] Failed to determine database version.", - "Attempted to check if the database version is compatible with the DNA version, but failed to do so. If you do not see any other warnings or errors, you can probably ignore this message. If it happens often, consider filing an issue on GitHub.", - e); - Dna.logger.log(l); - } - return compatible; - } - /** * Get the active coder. * From 0794114df7215665c89679d46aabc599e042e337 Mon Sep 17 00:00:00 2001 From: Krgaric <102651574+Krgaric@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:17:13 +0000 Subject: [PATCH 02/43] Updated bibliography --- bibliography/bibliography.bib | 156 ++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index b22939fa..679b8ea2 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -53,6 +53,28 @@ @article{anshori2023who doi={10.17576/jkmjc-2023-3902-20} } +@article{anwar2023dynamic, + title={Dynamic Governance Based Educational Tourism Development Model in {I}ndonesia}, + author={Anwar, Muhammad Khairul and Zauhar, Soesilo and Wanusmawatie, Ike}, + journal={Erudio Journal of Educational Innovation}, + volume={10}, + number={2}, + pages={165--175}, + year={2023}, + doi={10.5281/zenodo.10464872} +} + +@article{arifianto2024discourse, + title={A Discourse Network Analysis: How are Freelancers in {I}ndonesia Portrayed?}, + author={Arifianto, Chandra and Tajuddien, Rahadyan and Kustini, Endang and Putri, Syahrina}, + journal={Journal of Business Management and Economic Development}, + volume={2}, + number={1}, + pages={397--406}, + year={2024}, + doi={10.59653/jbmed.v2i01.556} +} + @article{aryal2022navigating, title={Navigating Policy Debates of and Discourse Coalitions on {N}epal's Scientific Forest Management}, author={Aryal, Kishor and Laudari, Hari Krishna and Maraseni, Tek and Pathak, Bhoj Raj}, @@ -231,6 +253,15 @@ @article{breindl2013discourse doi={10.1108/info-03-2013-0011} } +@mastersthesis{bresser2024transitions, + title={Transitions in the Risk Assessment Regime: Prospects from the Case of Animal-Free Testing in the Cosmetics Industry}, + author={Bresser, Ben}, + year={2024}, + school={Utrecth University, Innovation Sciences}, + type={Master's Thesis}, + url={https://studenttheses.uu.nl/bitstream/handle/20.500.12932/45751/Thesis_Ben_Bresser_Final.pdf} +} + @article{broadbent2016conflicting, title={Conflicting Climate Change Frames in a Global Field of Media Discourse}, author={Broadbent, Jeffrey and Sonnett, John and Botetzagias, Iosef and Carson, Marcus and Carvalho, Anabela and Chien, Yu-Ju and Edling, Christopher and Fisher, Dana R. and Giouzepas, Georgios and Haluza-DeLay, Randolph and Hazegawa, Koichi and Hirschi, Christian and Horta, Ana and Ikeda, Kazuhiro and Jin, Jun and Ku, Dowan and Lahsen, Myanna and Lee, Ho-Ching and Lin, Tze-Luen Alan and Malang, Thomas and Ollmann, Jana and Payne, Diane and Pellissery, Sony and Price, Stephen and Pulver, Simone and Sainz, Jaime and Satoh, Keiichi and Saunders, Clare and Schmidt, Luisa and Stoddart, Mark C. J. and Swarnakar, Pradip and Tatsumi, Tomoyuki and Tindall, David and Vaughter, Philip and Wagner, Paul and Yun, Sun-Jin and Zhengyi, Sun}, @@ -384,6 +415,18 @@ @incollection{dalimunthe2022depicting doi={10.1007/978-981-19-2738-6_9} } +@article{demler2023discourse, + title={Against All Odds? A Discourse Network Analysis of the Political Debate About the {G}erman Passenger Car Toll Act}, + author={Demler, Katja and Thurm, Stefanie}, + journal={{G}erman Politics}, + volume={33}, + pages={1--25}, + note={Forthcoming}, + year={2023}, + publisher={Taylor \& Francis}, + doi={10.1016/j.eist.2019.02.002} +} + @article{diaz2018competing, title={Competing Actors in the Climate Change Arena in {M}exico: A Network Analysis}, author={D{\'\i}az, Araceli Ortega and Guti{\'e}rrez, Erika Casamadrid}, @@ -775,6 +818,17 @@ @phdthesis{gutierrez2022redes doi={11651/5321} } +@article{gutierrez2024advocacy, + title={Advocacy coalitions, soft power, and policy change in Mexican electricity policy: a discourse network analysis}, + author={Guti{\'e}rrez-Meave, Ra{\'u}l}, + journal={Policy \& Politics}, + pages={1--20}, + year={2024}, + publisher={Policy Press}, + doi={10.1332/03055736Y2023D000000005}, + note={Fortcoming} +} + @article{hamanduna2023discourse, author={Hamanduna, Antonius O. Lapu and Widjanarko, Putut}, title={Discourse Network on the Revision of {I}ndonesian Information and Electronic Transaction Law}, @@ -935,6 +989,17 @@ @article{heiberg2022assessing doi={10.1016/j.respol.2021.104363} } +@article{heinmiller2023advocacy, + title={Have Advocacy Coalitions Be Difference-Making in {C}anadian Policy processes? Evidence from Firearms Policy Processes in the 1970s and 1990s}, + author={Heinmiller, Timothy B.}, + journal={Canadian Political Science Review}, + volume={17}, + number={2}, + pages={1--17}, + year={2023}, + doi={https://ojs.unbc.ca/index.php/cpsr} +} + @phdthesis{henrichsen2020party, title={Party Competition as Interdependent Process -- Assessing the Contagion Effect of {E}urosceptic Parties in {I}taly}, author={Henrichsen, Tim}, @@ -1495,6 +1560,29 @@ @article{li2023policy doi={10.3390/ijerph20065204} } +@article{li2023policy, + title={Policy Changes in China's Family Planning: Perspectives of Advocacy Coalitions}, + author={Li, Zhichao and Tan, Xihan and Liu, Bojia}, + journal={International Journal of Environmental Research and Public Health}, + volume={20}, + number={6}, + pages={5204}, + year={2023}, + doi={10.3390/ijerph20065204} +} + +@article{lintner2023slovak, + title={Slovak MPs’ Response to the 2022 {R}ussian Invasion of {U}kraine in Light of Conspiracy Theories and the Polarization of Political Discourse}, + author={Lintner, Tom{\'a}{\v{s}} and Divi{\'a}k, Tom{\'a}{\v{s}} and Nekardov{\'a}, Barbora and Lehotsk{\`y}, Luk{\'a}{\v{s}} and Va{\v{s}}e{\v{c}}ka, Michal}, + journal={Humanities and Social Sciences Communications}, + volume={10}, + number={1}, + pages={1--11}, + year={2023}, + publisher={Palgrave}, + doi={10.1057/s41599-023-02276-8} +} + @mastersthesis{livckova2023coal, title={Coal Framing in the {I}ndian Political Discourse}, author={Li{\v{c}}kov{\'a}, Vladim{\'\i}ra}, @@ -1752,6 +1840,30 @@ @book{nagel2015polarisierung doi={10.1007/978-3-658-11225-7} } +@incollection{nagel2023allianzen, + title={Allianzen und Strategien f\"ur saubere Luft in den St\"adten}, + author={Nagel, Melanie}, + year={2023}, + booktitle={Konflikte um Infrastrukturen: \"Offentliche Debatten und politische Konzepte}, + editor={Betz, Johanna and Bieling, Hans-J\"urgen and Futterer, Andrea and M\"ohring-Hesse, Matthias and Nagel, Melanie}, + publisher={transcript Verlag}, + address={Bielefeld}, + pages={153--177}, + doi={10.1515/9783839467428} +} + +@incollection{nagel2023divergierende, + title={Divergierende Interessen: Der Kampf um saubere Luft in den St\"adten}, + author={Nagel, Melanie}, + year={2023}, + booktitle={Konflikte um Infrastrukturen: \"Offentliche Debatten und politische Konzepte}, + editor={Betz, Johanna and Bieling, Hans-J\"urgen and Futterer, Andrea and M\"ohring-Hesse, Matthias and Nagel, Melanie}, + publisher={transcript Verlag}, + address={Bielefeld}, + pages={77--98}, + doi={10.1515/9783839467428} +} + @article{nagel2022analyzing, title={Analyzing Multi-Level Governance Dynamics from a Discourse Network Perspective: The Debate over Air Pollution Regulation in {G}ermany}, author={Nagel, Melanie and Bravo-Laguna, Carlos}, @@ -1894,6 +2006,16 @@ @article{ohno2022advocacy doi={10.4000/irpp.2362} } +@article{oliveira2023coalizoes, + author={Oliveira, Henrique C. and Sanches Filho, Alvino}, + title={Coaliz\~oes de Defesa na Pol\'itica de Transporte Mar\'itimo de Cargas no {B}rasil: Privatiza\c{c}\~ao, Descentraliza\c{c}\~ao e Abertura para o Capital Estrangeiro}, + journal={Caderno CRH}, + volume={36}, + pages={1--21}, + year={2023}, + doi={10.9771/ccrh.v36i0.55996} +} + @phdthesis{osei2021advocacy, title={An Advocacy Coalition Framework Analysis of Oil and Gas Governance in {G}hana}, author={Osei-Kojo, Alex}, @@ -2021,6 +2143,18 @@ @article{pratiwi2023discourse doi={10.25139/jkp.v7i2.5998} } +@article{putri2023advocacy, + title={The Advocacy Coalition of Sugar-Sweetened Beverage Taxes in {I}ndonesia}, + author={Putri, Rayssa A. and Alemmario, Reyhan and Melinda, Gea and Audwina, Ardiani H. and Espressivo, Aufia and Herlinda, Olivia and Meilissa, Yurdhina and Saminarsih, Diah S.}, + journal={BMJ Global Health}, + volume={8}, + number={Suppl 8}, + pages={1--13}, + year={2023}, + publisher={BMJ Publishing Group}, + doi={10.1136/bmjgh-2023-012052} +} + @article{rantala2014multistakeholder, title={Multistakeholder Environmental Governance in Action: {REDD}+ Discourse Coalitions in {T}anzania}, author={Rantala, Salla and Di Gregorio, Monica}, @@ -2166,6 +2300,17 @@ @article{rychlik2021come doi={10.1111/polp.12431} } +@article{saputro2023media, + title={Bias Media Mengenai Pemberitaan Batalnya {I}ndonesia Menjadi Tuan Rumah Piala Dunia U-20 2023 di Media Online Tirto.id dan Tempo.co}, + author={Saputro, Nugroho P. and Pearly, Claire and Hidayat, Elguarddine and Charoline, Elisabeth and Wulan, Roro R.}, + journal={Jurnal Ilmiah Wahana Pendidikan}, + volume={9}, + number={23}, + pages={547--556}, + year={2023}, + doi={https://jurnal.peneliti.net/index.php/JIWP/article/view/5655} +} + @phdthesis{schaub2021politics, title={The Politics of Water Protection}, author={Schaub, Simon}, @@ -2286,6 +2431,17 @@ @article{schmitz2022open doi={10.1111/jcms.13428} } +@article{schneider2024deconstruction, + title={The (De-)Construction of Technology Legitimacy: Contending Storylines Surrounding Wind Energy in {A}ustria and {S}witzerland}, + author={Schneider, Nina and Rinscheid, Adrian}, + journal={Technological Forecasting and Social Change}, + volume={198}, + pages={1--16}, + year={2024}, + publisher={Elsevier}, + doi={10.1016/j.techfore.2023.122929} +} + @incollection{schneider2013punctuations, title={Punctuations and Displacements in Policy Discourse: The Climate Change Issue in {G}ermany 2007--2010}, author={Schneider, Volker and Ollmann, Jana K.}, From 831b530b29be0c80ad86c37c205b5c0a5b5ab669 Mon Sep 17 00:00:00 2001 From: Krgaric <102651574+Krgaric@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:16:16 +0000 Subject: [PATCH 03/43] Fixed issue with bibliography update --- bibliography/bibliography.bib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index 679b8ea2..a60f481f 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -1572,7 +1572,7 @@ @article{li2023policy } @article{lintner2023slovak, - title={Slovak MPs’ Response to the 2022 {R}ussian Invasion of {U}kraine in Light of Conspiracy Theories and the Polarization of Political Discourse}, + title={Slovak MPs' Response to the 2022 {R}ussian Invasion of {U}kraine in Light of Conspiracy Theories and the Polarization of Political Discourse}, author={Lintner, Tom{\'a}{\v{s}} and Divi{\'a}k, Tom{\'a}{\v{s}} and Nekardov{\'a}, Barbora and Lehotsk{\`y}, Luk{\'a}{\v{s}} and Va{\v{s}}e{\v{c}}ka, Michal}, journal={Humanities and Social Sciences Communications}, volume={10}, From 8652e7f221ae324b2c4dd097528c98398c4aafe8 Mon Sep 17 00:00:00 2001 From: leifeld Date: Wed, 10 Jan 2024 20:52:39 +0000 Subject: [PATCH 04/43] Revised bibliography and added first steps for kernel-smoothed time slice export --- bibliography/bibliography.bib | 73 +-- build/bibliography.md | 721 +++++++++++++++---------- dna/src/main/java/export/Exporter.java | 168 +++++- dna/src/main/java/export/Matrix.java | 12 + 4 files changed, 631 insertions(+), 343 deletions(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index a60f481f..63954779 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -257,9 +257,9 @@ @mastersthesis{bresser2024transitions title={Transitions in the Risk Assessment Regime: Prospects from the Case of Animal-Free Testing in the Cosmetics Industry}, author={Bresser, Ben}, year={2024}, - school={Utrecth University, Innovation Sciences}, + school={Utrecht University, Innovation Sciences}, type={Master's Thesis}, - url={https://studenttheses.uu.nl/bitstream/handle/20.500.12932/45751/Thesis_Ben_Bresser_Final.pdf} + doi={20.500.12932/45751} } @article{broadbent2016conflicting, @@ -348,7 +348,7 @@ @phdthesis{cassiers2018politics url={https://kuleuven.limo.libis.be/discovery/fulldisplay?docid=lirias2000485&context=SearchWebhook&vid=32KUL_KUL:Lirias&search_scope=lirias_profile&tab=LIRIAS&adaptor=SearchWebhook&lang=en} } -@article{cerny2018limity, +@mastersthesis{cerny2018limity, title={Limity {\v{c}}esk{\'e} energetick{\'e} tranzice v politick{\'e} perspektiv{\v{e}}: p{\v{r}}{\'i}pad t{\v{e}}{\v{z}}by uhl{\'i}}, author={{\v{C}}ern{\`y}, Ond{\v{r}}ej}, year={2018}, @@ -389,8 +389,8 @@ @mastersthesis{coronado2015reforma title={La Reforma Tributaria 2014: Un an{\'a}lisis desde las coaliciones discursivas}, author={Coronado Vigueras, Rodrigo Alonso}, year={2015}, - publisher={Universidad de Chile, Facultad de Ciencias F{\'i}sicas y Matem{\'a}ticas}, type={Tesis Postgrado}, + school={Universidad de Chile, Facultad de Ciencias F{\'i}sicas y Matem{\'a}ticas}, url={https://repositorio.uchile.cl/handle/2250/137806} } @@ -418,12 +418,10 @@ @incollection{dalimunthe2022depicting @article{demler2023discourse, title={Against All Odds? A Discourse Network Analysis of the Political Debate About the {G}erman Passenger Car Toll Act}, author={Demler, Katja and Thurm, Stefanie}, + year={2023}, journal={{G}erman Politics}, volume={33}, - pages={1--25}, note={Forthcoming}, - year={2023}, - publisher={Taylor \& Francis}, doi={10.1016/j.eist.2019.02.002} } @@ -819,11 +817,10 @@ @phdthesis{gutierrez2022redes } @article{gutierrez2024advocacy, - title={Advocacy coalitions, soft power, and policy change in Mexican electricity policy: a discourse network analysis}, + title={Advocacy Coalitions, Soft Power, and Policy Change in {M}exican Electricity Policy: A Discourse Network Analysis}, author={Guti{\'e}rrez-Meave, Ra{\'u}l}, - journal={Policy \& Politics}, - pages={1--20}, year={2024}, + journal={Policy \& Politics}, publisher={Policy Press}, doi={10.1332/03055736Y2023D000000005}, note={Fortcoming} @@ -990,14 +987,14 @@ @article{heiberg2022assessing } @article{heinmiller2023advocacy, - title={Have Advocacy Coalitions Be Difference-Making in {C}anadian Policy processes? Evidence from Firearms Policy Processes in the 1970s and 1990s}, + title={Have Advocacy Coalitions been Difference-Making in {C}anadian Policy Processes? {E}vidence from Firearms Policy Processes in the 1970s and 1990s}, author={Heinmiller, Timothy B.}, journal={Canadian Political Science Review}, volume={17}, number={2}, pages={1--17}, year={2023}, - doi={https://ojs.unbc.ca/index.php/cpsr} + url={https://ojs.unbc.ca/index.php/cpsr/article/view/1877} } @phdthesis{henrichsen2020party, @@ -1186,7 +1183,6 @@ @article{joshi2022fair title={How Fair is Our Air? {T}he Injustice of Procedure, Distribution, and Recognition within the Discourse of Air Pollution in {D}elhi, {I}ndia}, author={Joshi, Bhavna and Swarnakar, Pradip}, journal={Environmental Sociology}, - pages={1--14}, year={2023}, publisher={Taylor \& Francis}, volume={9}, @@ -1203,7 +1199,6 @@ @article{joshi2021staying number={4}, pages={492--511}, year={2021}, - publisher={SAGE Publications Sage UK: London, England}, doi={10.1177/0011392121990023} } @@ -1531,11 +1526,13 @@ @mastersthesis{leipold2016oekonomische url={https://nbn-resolving.org/urn:nbn:de:hbz:708-dh324} } -@unpublished{lemke2023defining, - title={Defining Artificial Intelligence as a Political Problem: A Discourse Network Analysis from {G}ermany}, +@article{lemke2023defining, + title={Agenda-setting in Nascent Policy Subsystems: {I}ssue and Instrument Priorities across Venues}, author={Lemke, Nicole and Trein, Philipp and Varone, Fr{\'e}d{\'e}ric}, year={2023}, - url={https://www.philipptrein.com/wp-content/uploads/2023/01/Lemke_et_al_2022.pdf} + volume={56}, + pages={633--655}, + doi={10.1007/s11077-023-09514-5} } @article{lestrelin2017collaborative, @@ -1560,17 +1557,6 @@ @article{li2023policy doi={10.3390/ijerph20065204} } -@article{li2023policy, - title={Policy Changes in China's Family Planning: Perspectives of Advocacy Coalitions}, - author={Li, Zhichao and Tan, Xihan and Liu, Bojia}, - journal={International Journal of Environmental Research and Public Health}, - volume={20}, - number={6}, - pages={5204}, - year={2023}, - doi={10.3390/ijerph20065204} -} - @article{lintner2023slovak, title={Slovak MPs' Response to the 2022 {R}ussian Invasion of {U}kraine in Light of Conspiracy Theories and the Polarization of Political Discourse}, author={Lintner, Tom{\'a}{\v{s}} and Divi{\'a}k, Tom{\'a}{\v{s}} and Nekardov{\'a}, Barbora and Lehotsk{\`y}, Luk{\'a}{\v{s}} and Va{\v{s}}e{\v{c}}ka, Michal}, @@ -1841,11 +1827,11 @@ @book{nagel2015polarisierung } @incollection{nagel2023allianzen, - title={Allianzen und Strategien f\"ur saubere Luft in den St\"adten}, + title={Allianzen und {S}trategien f{\"u}r saubere {L}uft in den {S}t{\"a}dten}, author={Nagel, Melanie}, year={2023}, - booktitle={Konflikte um Infrastrukturen: \"Offentliche Debatten und politische Konzepte}, - editor={Betz, Johanna and Bieling, Hans-J\"urgen and Futterer, Andrea and M\"ohring-Hesse, Matthias and Nagel, Melanie}, + booktitle={Konflikte um {I}nfrastrukturen: {\"O}ffentliche {D}ebatten und politische {K}onzepte}, + editor={Betz, Johanna and Bieling, Hans-J{\"u}rgen and Futterer, Andrea and M{\"o}hring-Hesse, Matthias and Nagel, Melanie}, publisher={transcript Verlag}, address={Bielefeld}, pages={153--177}, @@ -1853,10 +1839,10 @@ @incollection{nagel2023allianzen } @incollection{nagel2023divergierende, - title={Divergierende Interessen: Der Kampf um saubere Luft in den St\"adten}, + title={Divergierende {I}nteressen: {D}er {K}ampf um saubere {L}uft in den {S}t\"adten}, author={Nagel, Melanie}, year={2023}, - booktitle={Konflikte um Infrastrukturen: \"Offentliche Debatten und politische Konzepte}, + booktitle={Konflikte um {I}nfrastrukturen: {\"O}ffentliche {D}ebatten und politische {K}onzepte}, editor={Betz, Johanna and Bieling, Hans-J\"urgen and Futterer, Andrea and M\"ohring-Hesse, Matthias and Nagel, Melanie}, publisher={transcript Verlag}, address={Bielefeld}, @@ -2008,11 +1994,12 @@ @article{ohno2022advocacy @article{oliveira2023coalizoes, author={Oliveira, Henrique C. and Sanches Filho, Alvino}, - title={Coaliz\~oes de Defesa na Pol\'itica de Transporte Mar\'itimo de Cargas no {B}rasil: Privatiza\c{c}\~ao, Descentraliza\c{c}\~ao e Abertura para o Capital Estrangeiro}, - journal={Caderno CRH}, + title={Coaliz{\~o}es de Defesa na Pol{\'i}tica de Transporte Mar{\'i}timo de Cargas no {B}rasil: Privatiza\c{c}{\~a}o, Descentraliza\c{c}{\~a}o e Abertura para o Capital Estrangeiro}, + year={2023}, + journal={Caderno CRH -- Revista de Ci{\^e}ncias Sociais do Centro de Estudos Pesquisas e Humanidades da Universidade Federal da Bahia}, volume={36}, + number={2}, pages={1--21}, - year={2023}, doi={10.9771/ccrh.v36i0.55996} } @@ -2121,14 +2108,14 @@ @mastersthesis{pospivsilova2022liberty url={https://is.muni.cz/th/azy84/} } -@article{pratama21discourse, +@article{pratama2017discourse, title={Discourse Networking Analysis as Alternative Research Method in Communication Science Studies -- Discourse Networking Analysis sebagai Metode Penelitian Alternatif dalam Kajian Ilmu Komunikasi}, author={Pratama, Bayu Indra and Illahi Ulfa, Adinda Ardwi}, + year={2017}, journal={Jurnal Penelitian Komunikasi dan Opini Publik}, volume={21}, number={2}, - pages={223278}, - publisher={Indonesian Ministry of Communication and Informatics}, + pages={126--136}, url={https://www.neliti.com/publications/223278/} } @@ -2308,7 +2295,7 @@ @article{saputro2023media number={23}, pages={547--556}, year={2023}, - doi={https://jurnal.peneliti.net/index.php/JIWP/article/view/5655} + url={https://jurnal.peneliti.net/index.php/JIWP/article/view/5655} } @phdthesis{schaub2021politics, @@ -2436,7 +2423,7 @@ @article{schneider2024deconstruction author={Schneider, Nina and Rinscheid, Adrian}, journal={Technological Forecasting and Social Change}, volume={198}, - pages={1--16}, + pages={122929}, year={2024}, publisher={Elsevier}, doi={10.1016/j.techfore.2023.122929} @@ -2511,7 +2498,7 @@ @incollection{selle2017multi doi={10.5771/9783845278032-149} } -@article{sevcik2021analyza, +@mastersthesis{sevcik2021analyza, title={Anal{\`y}za vy{\v{r}}azen{\'\i} jadern{\'e} energie z energetick{\'e}ho mixu {N}{\v{e}}mecka po roce 2011}, author={{\v{S}}ev{\v{c}}{\'i}k, Martin}, year={2021}, @@ -3276,9 +3263,7 @@ @article{you2021comparing title={Comparing Policy Conflict on Electricity Transmission Line Sitings}, author={You, Jongeun and Yordy, Jill and Weible, Christopher M. and Park, Kyudong and Heikkila, Tanya and Gilchrist, Duncan}, journal={Public Policy and Administration}, - pages={09520767211036800}, year={2021}, - publisher={SAGE Publications Sage UK: London, England}, volume={38}, number={1}, pages={107–-129}, diff --git a/build/bibliography.md b/build/bibliography.md index 388c0163..9c34e7be 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2023-10-15 +date: 2024-01-10 title: "Discourse Network Analysis: Bibliography" --- @@ -21,7 +21,7 @@ before contributing any citation entries. ------------------------------------------------------------------------
+entry-spacing="0" line-spacing="2">
@@ -70,9 +70,27 @@ construction. *Jurnal Komunikasi: Malaysian Journal of Communication*,
+
+ +6\. Anwar, M. K., Zauhar, S., & Wanusmawatie, I. (2023). Dynamic +governance based educational tourism development model in Indonesia. +*Erudio Journal of Educational Innovation*, *10*(2), 165–175. + + +
+ +
+ +7\. Arifianto, C., Tajuddien, R., Kustini, E., & Putri, S. (2024). A +discourse network analysis: How are freelancers in Indonesia portrayed? +*Journal of Business Management and Economic Development*, *2*(1), +397–406. + +
+
-6\. Aryal, K., Laudari, H. K., Maraseni, T., & Pathak, B. R. (2022). +8\. Aryal, K., Laudari, H. K., Maraseni, T., & Pathak, B. R. (2022). Navigating policy debates of and discourse coalitions on Nepal’s scientific forest management. *Forest Policy and Economics*, *141*, 102768. @@ -81,7 +99,7 @@ scientific forest management. *Forest Policy and Economics*, *141*,
-7\. Bandau, S. L. (2021). *Emerging institutions in the global space +9\. Bandau, S. L. (2021). *Emerging institutions in the global space sector. An institutional logics approach* \[Master's Thesis, Utrecht University, Copernicus Institute of Sustainable Development\]. @@ -90,7 +108,7 @@ University, Copernicus Institute of Sustainable Development\].
-8\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them +10\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them all? Narrating the agenda for labor market policies in France and Germany. *Policy and Society*, *38*(3), 408–428. @@ -99,7 +117,7 @@ Germany. *Policy and Society*, *38*(3), 408–428.
-9\. Barnickel, C. (2019). *Postdemokratisierung der +11\. Barnickel, C. (2019). *Postdemokratisierung der Legitimationspolitik: Diskursnetzwerke in bundesdeutschen Großen RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS. @@ -108,7 +126,7 @@ RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS.
-10\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: +12\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: Legitimationspolitik in der Großen RegierungserklĂ€rung der 19. Wahlperiode im Vergleich. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *14*(3), 199–223. @@ -118,7 +136,7 @@ Politikwissenschaft*, *14*(3), 199–223.
-11\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and +13\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and management integration in Germany, Spain and Sweden. *Land Use Policy*, *101*, 105182. @@ -126,7 +144,7 @@ management integration in Germany, Spain and Sweden. *Land Use Policy*,
-12\. Belova, A. (2021). *Another silver bullet for the energy +14\. Belova, A. (2021). *Another silver bullet for the energy transition? Discourse network analysis of German hydrogen debate* \[Master’s thesis, Department of International Relations; European Studies, Energy Policy Studies, Masaryk University\]. @@ -136,7 +154,7 @@ Studies, Energy Policy Studies, Masaryk University\].
-13\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & +15\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & Kemmerzell, J. (2023). The more the merrier? Actors and ideas in the evolution of German hydrogen policy discourse. *Energy Research & Social Science*, *97*, 102965. @@ -145,7 +163,7 @@ Science*, *97*, 102965.
-14\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse +16\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse network analysis as a novel approach to party control. *Politics and Governance*, *8*(2), 229–242. @@ -153,7 +171,7 @@ Governance*, *8*(2), 229–242.
-15\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a +17\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a tool to maintain party unity: The case of oppressed political conflict in German parliament debates on the Euro crisis. *Party Politics*, *29*(3), 554--569. @@ -162,7 +180,7 @@ in German parliament debates on the Euro crisis. *Party Politics*,
-16\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network +18\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network analysis of climate delayism in the Czech Republic* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. @@ -171,7 +189,7 @@ Masaryk University, Faculty of Social Studies\].
-17\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & +19\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & Padó, S. (2019). An environment for relational annotation of political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations*, 105–110. @@ -181,7 +199,7 @@ Computational Linguistics: System Demonstrations*, 105–110.
-18\. Boas, I. (2015). *Climate migration and security: Securitisation as +20\. Boas, I. (2015). *Climate migration and security: Securitisation as a strategy in climate change politics*. Routledge. @@ -189,7 +207,7 @@ a strategy in climate change politics*. Routledge.
-19\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice +21\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice o energetickej efektĂ­vnosti – analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -198,7 +216,7 @@ Thesis, Masaryk University, Faculty of Social Studies\].
-20\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual +22\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual screening: Analyzing roles, content and motivations in political Twitter conversations. *Politics and Governance*, *8*(2), 311–325. @@ -207,7 +225,7 @@ conversations. *Politics and Governance*, *8*(2), 311–325.
-21\. Brandenberger, L. (2019). Predicting network events to assess +23\. Brandenberger, L. (2019). Predicting network events to assess goodness of fit of relational event models. *Political Analysis*, *27*(4), 556–571. @@ -215,15 +233,24 @@ goodness of fit of relational event models. *Political Analysis*,
-22\. Breindl, Y. (2013). Discourse networks on state-mandated access +24\. Breindl, Y. (2013). Discourse networks on state-mandated access blocking in Germany and France. *Info*, *15*(6), 42–62.
+
+ +25\. Bresser, B. (2024). *Transitions in the risk assessment regime: +Prospects from the case of animal-free testing in the cosmetics +industry* \[Master's Thesis, Utrecht University, Innovation Sciences\]. + + +
+
-23\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, +26\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, A., Chien, Y.-J., Edling, C., Fisher, D. R., Giouzepas, G., Haluza-DeLay, R., Hazegawa, K., Hirschi, C., Horta, A., Ikeda, K., Jin, J., Ku, D., Lahsen, M., Lee, H.-C., Lin, T.-L. A., 
 Zhengyi, S. (2016). @@ -235,7 +262,7 @@ Conflicting climate change frames in a global field of media discourse.
-24\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis +27\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis of climate change and society: A network approach. In M. J. Manfredo, J. J. Vaske, A. Rechkemmer, & E. A. Duke (Eds.), *Understanding society and natural resources. Forging new strands of integration across the social @@ -246,7 +273,7 @@ sciences* (pp. 203–228). Springer Open.
-25\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: +28\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: Making sense of the international discourse on transfer pricing methodologies. *Review of International Political Economy*, *29*(1), 307–335. @@ -255,7 +282,7 @@ methodologies. *Review of International Political Economy*, *29*(1),
-26\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse +29\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse networks on local energy transitions. *Environmental Innovation and Societal Transitions*, *39*, 141–154. @@ -264,7 +291,7 @@ Societal Transitions*, *39*, 141–154.
-27\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die +30\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die US-Außenpolitik. Ein Vergleich der Israelpolitik zwischen den Kabinetten Obama II und Trump* \[Master's Thesis, Leopold-Franzens-UniversitĂ€t Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\]. @@ -274,7 +301,7 @@ Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\].
-28\. Brutschin, E. (2013). *Dynamics in EU policy-making: The +31\. Brutschin, E. (2013). *Dynamics in EU policy-making: The liberalization of the European gas market* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -283,7 +310,7 @@ University of Konstanz, Department of Politics; Public Administration\].
-29\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A +32\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A discourse network analysis of UK newspaper coverage of the “sugar tax” debate before and after the announcement of the soft drinks industry levy. *BMC Public Health*, *19*(490), 1–14. @@ -293,7 +320,7 @@ levy. *BMC Public Health*, *19*(490), 1–14.
-30\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of +33\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of territoriality in policy networks. The case of transportation policies in the cross-border metropolitan regions of Brussels and Luxembourg* \[Doctoral Dissertation, KU Leuven, Division of Geography; Tourism\]. @@ -303,15 +330,16 @@ in the cross-border metropolitan regions of Brussels and Luxembourg*
-31\. Černỳ, O. (2018). *Limity českĂ© +34\. Černỳ, O. (2018). *Limity českĂ© energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­* -\[Diploma Thesis\]. +\[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. +
-32\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media +35\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media coverage: A case of the Czech coal policy. *Politics and Governance*, *8*(2), 272–285. @@ -319,7 +347,7 @@ coverage: A case of the Czech coal policy. *Politics and Governance*,
-33\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej +36\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej politike Českej republiky* \[Master's Thesis, Masarykova univerzita, Fakulta sociĂĄlnı́ch studiı́\]. @@ -327,7 +355,7 @@ Fakulta sociĂĄlnı́ch studiı́\].
-34\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green +37\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green networks in the Basque country between 2007 and 2017* \[PhD thesis, University of Trento, School of Social Sciences\]. @@ -336,7 +364,7 @@ University of Trento, School of Social Sciences\].
-35\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un +38\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un anĂĄlisis desde las coaliciones discursivas* \[Tesis Postgrado, Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\]. @@ -345,7 +373,7 @@ Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\].
-36\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking +39\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking in unison? Explaining the role of agenda-setter constellations in the ECB policy agenda using a network-based approach. *Journal of European Public Policy*. @@ -354,7 +382,7 @@ Public Policy*.
-37\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). +40\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). Depicting mangrove’s potential as blue carbon champion in indonesia. In R. Dasgupta, S. Hashimoto, & O. Saito (Eds.), *Assessing, mapping and modelling of mangrove ecosystem services in the asia-pacific region* @@ -362,9 +390,18 @@ modelling of mangrove ecosystem services in the asia-pacific region*
+
+ +41\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse +network analysis of the political debate about the German passenger car +toll act. *German Politics*, *33*. + + +
+
-38\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the +42\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the climate change arena in Mexico: A network analysis. *Journal of Environmental Management*, *215*, 239–247. @@ -373,7 +410,7 @@ Environmental Management*, *215*, 239–247.
-39\. Drozdzynski, F. A. (2022). *The common agricultural policy post +43\. Drozdzynski, F. A. (2022). *The common agricultural policy post 2020: An analysis of the beliefs of selected key stakeholders* \[Master's Thesis, University of Twente, Faculty of Behavioural, Management; Social Sciences\]. @@ -382,7 +419,7 @@ Management; Social Sciences\].
-40\. Duygan, M. (2018). *An actor-based analysis of political context +44\. Duygan, M. (2018). *An actor-based analysis of political context for supporting sustainability transitions of socio-technical systems: A study of Swiss waste management* \[Doctoral Dissertation, ETH ZĂŒrich, Department of Environmental Systems Science\]. @@ -392,7 +429,7 @@ Department of Environmental Systems Science\].
-41\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for +45\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for conceptualizing and uncovering the determinants of agency in socio-technical transitions. *Environmental Innovation and Societal Transitions*, *33*, 13–29. @@ -401,7 +438,7 @@ Transitions*, *33*, 13–29.
-42\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse +46\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse coalitions in Swiss waste management: Gridlock or winds of change? *Waste Management*, *72*, 25–44. @@ -410,7 +447,7 @@ coalitions in Swiss waste management: Gridlock or winds of change?
-43\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes +47\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes agency? Determinants of actors’ influence on formal institutions in Swiss waste management. *Technological Forecasting and Social Change*, *162*, 120413. @@ -419,7 +456,7 @@ Swiss waste management. *Technological Forecasting and Social Change*,
-44\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How +48\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How discourse network analysis (DNA) can help CSR research to investigate the “new” political role of corporations. In M. Nagel, P. Kenis, P. Leifeld, & H.-J. Schmedes (Eds.), *Politische komplexitĂ€t, governance @@ -430,14 +467,14 @@ von innovationen und policy-netzwerke* (pp. 139–146). Springer VS.
-45\. Eder, F. (2015). *Der irakkrieg 2003*. Innsbruck University Press. +49\. Eder, F. (2015). *Der irakkrieg 2003*. Innsbruck University Press.
-46\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. +50\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. Ostermann (Eds.), *Routledge handbook of foreign policy analysis methods* (pp. 516–535). Taylor & Francis. @@ -446,7 +483,7 @@ methods* (pp. 516–535). Taylor & Francis.
-47\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, +51\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*, *15*(1), 21–42. @@ -454,7 +491,7 @@ discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*,
-48\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting +52\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting counter-terrorism: Discourse networks and the politicisation of counter-terrorism in Austria. *Journal of International Relations and Development*, *24*(1), 171–195. @@ -464,7 +501,7 @@ Development*, *24*(1), 171–195.
-49\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan +53\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan komunikator publik dalam berita omicron baru di media online. *Jurnal Riset Komunikasi*, *6*(1), 58–79. @@ -473,7 +510,7 @@ Riset Komunikasi*, *6*(1), 58–79.
-50\. Elislah, N. (2023). Discourse network analysis on delaying +54\. Elislah, N. (2023). Discourse network analysis on delaying elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2), 225–240. @@ -481,7 +518,7 @@ elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2),
-51\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue +55\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue debate: A study on Covid-19 cases in Indonesia. *Jurnal Komunikasi: Malaysian Journal of Communication*, *36*(3), 209–227. @@ -490,7 +527,7 @@ Malaysian Journal of Communication*, *36*(3), 209–227.
-52\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping +56\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping discourse coalitions in the minimum unit pricing for alcohol debate: A discourse network analysis of UK newspaper coverage. *Addiction*, *114*(4), 741–753. @@ -499,7 +536,7 @@ discourse network analysis of UK newspaper coverage. *Addiction*,
-53\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological +57\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological tensions in education policy networks: An analysis of the policy innovators in education network in the United States. *Foro de Educacion*, *19*(1), 11–28. @@ -508,7 +545,7 @@ Educacion*, *19*(1), 11–28.
-54\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban +58\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban food strategies. In C. Tornaghi (Ed.), *Re-imagining sustainable food planning, building resourcefulness: Food movements, insurgent planning and heterodox economics: Proceedings of the 8th annual conference AESOP @@ -519,7 +556,7 @@ https://doi.org/ -55\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate +59\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate policy blockage. *Climatic Change*, *155*(4), 469–487. @@ -527,7 +564,7 @@ policy blockage. *Climatic Change*, *155*(4), 469–487.
-56\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the +60\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the ideological networks of American climate politics. *Climatic Change*, *116*(3), 523–545. @@ -535,7 +572,7 @@ ideological networks of American climate politics. *Climatic Change*,
-57\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does +61\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does political polarization come from? Locating polarization within the US climate change debate. *American Behavioral Scientist*, *57*(1), 70–92. @@ -544,7 +581,7 @@ climate change debate. *American Behavioral Scientist*, *57*(1), 70–92.
-58\. Friis, G. (2020). *Populist radical right parties into parliament: +62\. Friis, G. (2020). *Populist radical right parties into parliament: Changes in mainstream parties’ political positions in parliamentary debates on immigration and refugees* \[Master’s thesis, Uppsala University, Disciplinary Domain of Humanities; Social Sciences, Faculty @@ -555,7 +592,7 @@ of Social Sciences, Department of Government\].
-59\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network +63\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network analysis to understand ideological convergence and change in educational subsystems. *Education Policy Analysis Archives*, *28*(118). @@ -564,7 +601,7 @@ subsystems. *Education Policy Analysis Archives*, *28*(118).
-60\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). +64\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). Building consensus: Idea brokerage in teacher policy networks. *American Educational Research Journal*, *57*(2), 872–905. @@ -573,7 +610,7 @@ Educational Research Journal*, *57*(2), 872–905.
-61\. Galli Robertson, A. M. (2021). Privileged accounts in the debate +65\. Galli Robertson, A. M. (2021). Privileged accounts in the debate over coal-fired power in the United States. *Society & Natural Resources*, *34*(2), 188–207. @@ -582,7 +619,7 @@ Resources*, *34*(2), 188–207.
-62\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable +66\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable country: Agenda-setting and a discourse network approach on Philippine broadsheet media* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -592,7 +629,7 @@ Science, Oeschger Centre for Climate Change Research\].
-63\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The +67\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The politics of climate finance: Consensus and partisanship in designing green state investment banks in the United Kingdom and Australia. *Energy Research & Social Science*, *69*, 101583. @@ -602,7 +639,7 @@ green state investment banks in the United Kingdom and Australia.
-64\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local +68\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local policymakers’ attitudes towards climate change: A multi-method case study. *Social Networks*, *25*, 197–209. @@ -611,7 +648,7 @@ study. *Social Networks*, *25*, 197–209.
-65\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates +69\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates and agricultural policies: Discourse coalitions behind the creation of Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80. @@ -620,7 +657,7 @@ Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80.
-66\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social +70\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social innovation: Analysing policy debates in Japan. *Journal of Social Entrepreneurship*. @@ -628,7 +665,7 @@ Entrepreneurship*.
-67\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate +71\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate change in Italy: A discourse network analysis. *Politics and Governance*, *8*(2), 215–228. @@ -636,7 +673,7 @@ Governance*, *8*(2), 215–228.
-68\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and +72\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and right: A discourse network analysis of universal basic income on Dutch Twitter. *Journal of Social Policy*. @@ -645,7 +682,7 @@ Twitter. *Journal of Social Policy*.
-69\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage +73\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4), 490–514. @@ -653,7 +690,7 @@ in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4),
-70\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of +74\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of cultural resistance to a speed limit on German highways* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -662,7 +699,7 @@ Development\].
-71\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, +75\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, C. (2022). Discourse network analysis of nuclear narratives. In M. D. Jones, M. K. McBeth, & E. Shanahan (Eds.), *Narratives and the policy process: Applications of the narrative policy framework* (pp. 13–38). @@ -672,7 +709,7 @@ Montana State University Library.
-72\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations +76\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations of the mexican power generation policy subsystem. In A.-M. Bercu, I. Bilan, & C.-M. Apostoaie (Eds.), *European administrative area: Integration and resilience dynamics. Proceedings of the international @@ -684,16 +721,25 @@ Ioan Cuza” din Iai.
-73\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y +77\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y decisiones: La polĂ­tica de generaciĂłn elĂ©ctrica en mĂ©xico 1994–2018* \[PhD thesis, Centro de InvestigaciĂłn y Docencia EconĂłmicas (CIDE), Doctorado en PolĂ­ticas PĂșblicas\].
+
+ +78\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and +policy change in Mexican electricity policy: A discourse network +analysis. *Policy & Politics*. + + +
+
-74\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on +79\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on the revision of Indonesian information and electronic transaction law. *Jurnal Studi Komunikasi*, *7*(2), 519–538. @@ -702,7 +748,7 @@ the revision of Indonesian information and electronic transaction law.
-75\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics +80\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics after the “great recession.” Investigating the impact of economic shocks on EU climate policy-making in three case studies* \[Doctoral Dissertation, University of Potsdam, Faculty of Economics; Social @@ -712,7 +758,7 @@ Sciences\].
-76\. Hanschmann, R. (2017). Polarized business interests. EU climate +81\. Hanschmann, R. (2017). Polarized business interests. EU climate policy-making during the “great recession.” In D. K. Jesuit & R. A. Williams (Eds.), *Public policy, governance and polarization. Making governance work* (1st ed., pp. 126–156). Routledge. @@ -722,7 +768,7 @@ governance work* (1st ed., pp. 126–156). Routledge.
-77\. Hasselbalch, J. (2017). *The contentious politics of disruptive +82\. Hasselbalch, J. (2017). *The contentious politics of disruptive innovation: Vaping and fracking in the European Union* \[PhD thesis, University of Warwick, Department of Politics; International Studies; Université Libre de Bruxelles, Département de Sciences Politiques\]. @@ -732,7 +778,7 @@ Université Libre de Bruxelles, Département de Sciences Politiques\].
-78\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity +83\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity and skills in European labor mobility. *Review of International Political Economy*, *26*(6), 1333–1360. @@ -741,7 +787,7 @@ Political Economy*, *26*(6), 1333–1360.
-79\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke +84\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau (Eds.), *Capitalism and its legitimacy in times of crisis* (pp. 191–220). Palgrave Macmillan. @@ -751,7 +797,7 @@ without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau
-80\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der +85\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der Atomenergie: Diskursnetzwerkanalyse als Beitrag zur ErklĂ€rung einer radikalen Politikwende. *Zeitschrift fĂŒr Diskursforschung / Journal for Discourse Studies*, *1*(3), 288–316. @@ -761,7 +807,7 @@ Discourse Studies*, *1*(3), 288–316.
-81\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of +86\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of political discourse dynamics and the case of Germany’s nuclear energy phase-out. *Network Science*, *11*(2), 205–223. @@ -770,7 +816,7 @@ phase-out. *Network Science*, *11*(2), 205–223.
-82\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, +87\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, E., & Lapesa, G. (2020). Integrating manual and automatic annotation for the creation of discourse network data sets. *Politics and Governance*, *8*(2), 326–339. @@ -779,7 +825,7 @@ the creation of discourse network data sets. *Politics and Governance*,
-83\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). +88\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). Finanzkrise ohne Legitimationskrise? Kapitalismuskritik in der deutschen QualitĂ€tspresse. In M. Dammayr, D. Grass, & B. RothmĂŒller (Eds.), *LegitimitĂ€t. Gesellschaftliche, politische und wissenschaftliche @@ -790,7 +836,7 @@ Bruchlinien in der Rechtfertigung* (pp. 73–94). Transcript.
-84\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive +89\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive practices and the “need” for positive behaviour support (PBS): A critical discourse examination of disability policy beliefs. *Journal of Intellectual Disabilities*, *27*(1), 170–189. @@ -800,7 +846,7 @@ Intellectual Disabilities*, *27*(1), 170–189.
-85\. Heiberg, J. (2022). *The geography of configurations that work* +90\. Heiberg, J. (2022). *The geography of configurations that work* \[Doctoral Dissertation, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -808,7 +854,7 @@ Sustainable Development\].
-86\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of +91\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of technology legitimation: How multiscalar institutional dynamics matter for path creation in emerging industries. *Economic Geography*, *96*(5), 470–498. @@ -817,16 +863,26 @@ for path creation in emerging industries. *Economic Geography*, *96*(5),
-87\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions +92\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions through socio-technical configuration analysis – a methodological framework and a case study in the water sector. *Research Policy*, *51*(1), 104363.
+
+ +93\. Heinmiller, T. B. (2023). Have advocacy coalitions been +difference-making in Canadian policy processes? Evidence from firearms +policy processes in the 1970s and 1990s. *Canadian Political Science +Review*, *17*(2), 1–17. + + +
+
-88\. Henrichsen, T. (2020). *Party competition as interdependent process +94\. Henrichsen, T. (2020). *Party competition as interdependent process – assessing the contagion effect of Eurosceptic parties in Italy* \[PhD thesis, Sant’Anna School of Advanced Studies Pisa, Joint PhD in Political Science, European Politics; International Relations\]. @@ -836,7 +892,7 @@ Political Science, European Politics; International Relations\].
-89\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global +95\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global governance: Intersecting politicizations and the reform of investor–state arbitration. *Politics and Governance*, *8*(1), 336–347. @@ -845,7 +901,7 @@ investor–state arbitration. *Politics and Governance*, *8*(1), 336–347.
-90\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in +96\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in der Plattformökonomie. Herausforderungen des “coalition building” bei der Kooperation zwischen IG Metall und YouTubers Union. *Zeitschrift fĂŒr Soziologie*, *51*(2), 174–192. @@ -854,7 +910,7 @@ Soziologie*, *51*(2), 174–192.
-91\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, +97\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, P. (2020). Policy congruence and advocacy strategies in the discourse networks of minimum unit pricing for alcohol and the soft drinks industry levy. *Addiction*, *115*(12), 2303–2314. @@ -864,7 +920,7 @@ industry levy. *Addiction*, *115*(12), 2303–2314.
-92\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing +98\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing states’ messages about common core instruction: An analysis of English/language arts and close reading resources. *Teachers College Record*, *122*(3), 1–42. @@ -873,7 +929,7 @@ Record*, *122*(3), 1–42.
-93\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: +99\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -882,7 +938,7 @@ Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta*
-94\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in +100\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in Canada’s parliamentary system* \[PhD thesis, Simon Fraser University, Department of Political Science\]. @@ -890,7 +946,7 @@ Department of Political Science\].
-95\. Horning, D. G. (2017). *Understanding structure and character in +101\. Horning, D. G. (2017). *Understanding structure and character in rural water governance networks* \[PhD thesis, University of British Columbia, College of Graduate Studies\]. @@ -898,7 +954,7 @@ Columbia, College of Graduate Studies\].
-96\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). +102\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). Programmatisches Handeln in der deutschen Verkehrspolitik. Gemeinsame IdentitĂ€ten von Akteuren im Umfeld des Deutschlandtakts. In D. Sack, H. Straßheim, & K. Zimmermann (Eds.), *Renaissance der Verkehrspolitik. @@ -909,7 +965,7 @@ Springer VS.
-97\. Howe, A. C. (2022). *Network processes related to political +103\. Howe, A. C. (2022). *Network processes related to political discourse and policy positions: The case of climate change policy networks in Canada* \[PhD thesis, University of British Columbia, Department of Sociology\]. @@ -918,7 +974,7 @@ Department of Sociology\].
-98\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media +104\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media coverage and perceived policy influence of environmental actors: Good strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310. @@ -927,7 +983,7 @@ strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310.
-99\. Hullmann, C. (2023). *Case study on the German discourse of +105\. Hullmann, C. (2023). *Case study on the German discourse of industry decarbonization* \[Master's Thesis, Radboud University Nijmegen, Nijmegen School of Management\]. @@ -936,7 +992,7 @@ Nijmegen, Nijmegen School of Management\].
-100\. Hurka, S., & Nebel, K. (2013). Framing and policy change after +106\. Hurka, S., & Nebel, K. (2013). Framing and policy change after shooting rampages: A comparative analysis of discourse networks. *Journal of European Public Policy*, *20*(3), 390–406. @@ -945,7 +1001,7 @@ shooting rampages: A comparative analysis of discourse networks.
-101\. Imbert, I. (2017). *An inquiry into the material and ideational +107\. Imbert, I. (2017). *An inquiry into the material and ideational dimensions of policymaking: A case study of fuel poverty in Germany* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -955,7 +1011,7 @@ Public Administration\].
-102\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja +108\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja kannatuskoalitiot varhaiskasvatuksen diskurssiverkostossa* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -964,7 +1020,7 @@ Thesis, University of Helsinki, Faculty of Social Sciences\].
-103\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). +109\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). Diskursnetzwerkanalyse. Überlegungen zur Theoriebildung und Methodik. In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. @@ -974,7 +1030,7 @@ V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.),
-104\. Jeong, M. (2017). *National renewable energy policy in a global +110\. Jeong, M. (2017). *National renewable energy policy in a global world* \[PhD thesis, University of Maryland, College Park, School of Public Policy\]. @@ -983,7 +1039,7 @@ Public Policy\].
-105\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China +111\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China have a public debate on genetically modified organisms? A discourse network analysis of public debate on Weibo. *Public Understanding of Science*, *31*(6), 732–750. @@ -992,7 +1048,7 @@ Science*, *31*(6), 732–750.
-106\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The +112\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The injustice of procedure, distribution, and recognition within the discourse of air pollution in Delhi, India. *Environmental Sociology*, *9*(2), 176–189. @@ -1001,7 +1057,7 @@ discourse of air pollution in Delhi, India. *Environmental Sociology*,
-107\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: +113\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: Exploring risk and stigma of COVID-19 in the context of beliefs, actors and hierarchies in India. *Current Sociology*, *69*(4), 492–511. @@ -1010,7 +1066,7 @@ and hierarchies in India. *Current Sociology*, *69*(4), 492–511.
-108\. Kammerer, M. (2017). *Climate politics at the intersection between +114\. Kammerer, M. (2017). *Climate politics at the intersection between international dynamics and national decision-making: A policy network approach* \[Doctoral Thesis, University of Zurich, Faculty of Arts; Social Sciences\]. @@ -1019,7 +1075,7 @@ Social Sciences\].
-109\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die +115\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die EU: Eine Diskursperspektive auf die deutsche und schweizerische Klimapolitik. In R. Careja, P. Emmenegger, & N. Giger (Eds.), *The european social model under pressure. Liber amicorum in honour of klaus @@ -1030,7 +1086,7 @@ armingeon* (pp. 599–623). Springer VS.
-110\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate +116\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate change policy: The maturation of a policy discourse in the national and international context. *Social Networks*, *75*, 65–77. @@ -1039,7 +1095,7 @@ international context. *Social Networks*, *75*, 65–77.
-111\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat +117\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat Jakarta-Bandung dalam media online. *Jurnal Ilmu Komunikasi UHO: Jurnal Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34. @@ -1048,7 +1104,7 @@ Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34.
-112\. Keller, S. (2023). *Analysis of the media discourse about meat and +118\. Keller, S. (2023). *Analysis of the media discourse about meat and meat substitutes in U.S. Media between 2016 and 2021* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -1058,7 +1114,7 @@ Change Research\].
-113\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: +119\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: Policy network analysis. In H. Van den Bulck, M. Puppis, K. Donders, & L. Van Audenhove (Eds.), *The Palgrave handbook of methods for media policy research* (pp. 471–491). Palgrave Macmillan. @@ -1068,7 +1124,7 @@ policy research* (pp. 471–491). Palgrave Macmillan.
-114\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme +120\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme digital dalam perdebatan isu “presiden tiga periode” di Twitter. *Jurnal Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94. @@ -1077,7 +1133,7 @@ Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94.
-115\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? +121\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? Distributed-Ledger-Technologien im politischen Diskurs. Leitbilder, Ideen und Diskursnetzwerke im deutschen Bundestag der 19. Legislaturperiode* \[Master's Thesis, FU Berlin, Otto-Suhr-Institut fĂŒr @@ -1087,7 +1143,7 @@ Politikwissenschaft\].
-116\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech +122\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech Republic: Individual responsibility or structural issue? *Energy Research & Social Science*, *72*, 101877. @@ -1096,7 +1152,7 @@ Research & Social Science*, *72*, 101877.
-117\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling +123\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling environmental governance in the Lake Tahoe basin: A multiplex network approach. In M. Fischer & K. Ingold (Eds.), *Networks in water governance* (pp. 173–202). Palgrave Macmillan. @@ -1106,7 +1162,7 @@ governance* (pp. 173–202). Palgrave Macmillan.
-118\. Kooistra, M. N. (2022). *Space security and orbital +124\. Kooistra, M. N. (2022). *Space security and orbital sustainability. An institutional logics approach* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -1115,7 +1171,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-119\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). +125\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). Animals and climate change: A visual and discourse network analysis of Instagram posts. *Environmental Sociology*, *9*(4), 409–426. @@ -1124,7 +1180,7 @@ Instagram posts. *Environmental Sociology*, *9*(4), 409–426.
-120\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors +126\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors and justifications in media debates on Arctic climate change in Finland and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117. @@ -1133,7 +1189,7 @@ and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117.
-121\. Kukkonen, A. (2018). *Discourse networks and justifications of +127\. Kukkonen, A. (2018). *Discourse networks and justifications of climate change policy: News media debates in Canada, the United States, Finland, France, Brazil and India* \[Doctoral Dissertation, University of Helsinki, Faculty of Social Sciences\]. @@ -1143,7 +1199,7 @@ of Helsinki, Faculty of Social Sciences\].
-122\. Kukkonen, A., & MalkamÀki, A. (2023). A cultural approach to +128\. Kukkonen, A., & MalkamÀki, A. (2023). A cultural approach to politicization of science: How the forestry coalition challenged the scientific consensus in the Finnish news media debate on increased logging. *Society & Natural Resources*. @@ -1153,7 +1209,7 @@ logging. *Society & Natural Resources*.
-123\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy +129\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy interface as a discourse network: Finland’s climate change policy 2002–2015. *Politics and Governance*, *8*(2), 200. @@ -1162,7 +1218,7 @@ interface as a discourse network: Finland’s climate change policy
-124\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy +130\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy coalitions, beliefs and climate change policy in the United States. *Public Administration*, *95*(3), 713–729. @@ -1171,7 +1227,7 @@ coalitions, beliefs and climate change policy in the United States.
-125\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., +131\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., Lahsen, M., & Stoddart, M. C. J. (2018). International organizations, advocacy coalitions, and domestication of global norms: Debates on climate change in Canada, the US, Brazil, and India. *Environmental @@ -1182,7 +1238,7 @@ Science & Policy*, *81*, 54–62.
-126\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., +132\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., Kuhn, J., & PadĂł, S. (2020). DEbateNet-mig15: Tracing the 2015 immigration debate in Germany over time. *Proceedings of the Twelfth Language Resources and Evaluation Conference*, 919–927. @@ -1192,7 +1248,7 @@ Language Resources and Evaluation Conference*, 919–927.
-127\. Laurer, M., & Seidl, T. (2021). Regulating the European +133\. Laurer, M., & Seidl, T. (2021). Regulating the European data-driven economy: A case study on the general data protection regulation. *Policy & Internet*, *13*(2), 257–277. @@ -1201,7 +1257,7 @@ regulation. *Policy & Internet*, *13*(2), 257–277.
-128\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem +134\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem Discourse Network Analyzer (DNA). In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. 391–404). Springer VS. @@ -1211,7 +1267,7 @@ Visualisierungen* (pp. 391–404). Springer VS.
-129\. Leifeld, P. (2013). Reconceptualizing major policy change in the +135\. Leifeld, P. (2013). Reconceptualizing major policy change in the advocacy coalition framework: A discourse network analysis of German pension politics. *Policy Studies Journal*, *41*(1), 169–198. @@ -1220,7 +1276,7 @@ pension politics. *Policy Studies Journal*, *41*(1), 169–198.
-130\. Leifeld, P. (2014). Polarization of coalitions in an agent-based +136\. Leifeld, P. (2014). Polarization of coalitions in an agent-based model of political discourse. *Computational Social Networks*, *1*(1), 1–22. @@ -1228,7 +1284,7 @@ model of political discourse. *Computational Social Networks*, *1*(1),
-131\. Leifeld, P. (2016). *Policy debates as dynamic networks: German +137\. Leifeld, P. (2016). *Policy debates as dynamic networks: German pension politics and privatization discourse*. Campus. @@ -1236,7 +1292,7 @@ pension politics and privatization discourse*. Campus.
-132\. Leifeld, P. (2017). Discourse network analysis: Policy debates as +138\. Leifeld, P. (2017). Discourse network analysis: Policy debates as dynamic networks. In J. N. Victor, A. H. Montgomery, & M. N. Lubell (Eds.), *The Oxford handbook of political networks* (pp. 301–325). Oxford University Press. @@ -1246,7 +1302,7 @@ Oxford University Press.
-133\. Leifeld, P. (2020). Policy debates and discourse network analysis: +139\. Leifeld, P. (2020). Policy debates and discourse network analysis: A research agenda. *Politics and Governance*, *8*(2), 180–183. @@ -1254,7 +1310,7 @@ A research agenda. *Politics and Governance*, *8*(2), 180–183.
-134\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition +140\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition formation in policy debates*. arXiv Preprint. @@ -1262,7 +1318,7 @@ formation in policy debates*. arXiv Preprint.
-135\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and +141\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and the conflict over software patents in Europe. *European Journal of Political Research*, *51*(3), 382–409. @@ -1271,7 +1327,7 @@ Political Research*, *51*(3), 382–409.
-136\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. +142\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. (2021). Belief system alignment and cross-sectoral advocacy efforts in policy debates. *Journal of European Public Policy*, 1–24. @@ -1280,7 +1336,7 @@ policy debates. *Journal of European Public Policy*, 1–24.
-137\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss +143\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss von Diskurskoalitionen auf Vermögensbesteuerung in Deutschland, 1995–2015: Eine Diskursnetzwerkanalyse von Policy-Wandel in der Steuerpolitik* \[Master's Thesis, FernUniversitĂ€t Hagen, FakultĂ€t fĂŒr @@ -1291,16 +1347,15 @@ Kultur- und Sozialwissenschaften, Institut fĂŒr Politikwissenschaft\].
-138\. Lemke, N., Trein, P., & Varone, F. (2023). *Defining artificial -intelligence as a political problem: A discourse network analysis from -Germany*. - +144\. Lemke, N., Trein, P., & Varone, F. (2023). *Agenda-setting in +nascent policy subsystems: Issue and instrument priorities across +venues*. *56*, 633–655.
-139\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., +145\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., Lagabrielle, E., Seen, D. L., & Degenne, P. (2017). Collaborative landscape research in Reunion Island: Using spatial modelling and simulation to support territorial foresight and urban planning. *Applied @@ -1310,16 +1365,26 @@ Geography*, *78*, 66–77.
-140\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s +146\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s family planning: Perspectives of advocacy coalitions. *International Journal of Environmental Research and Public Health*, *20*(6), 5204.
+
+ +147\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, +M. (2023). Slovak MPs’ response to the 2022 Russian invasion of Ukraine +in light of conspiracy theories and the polarization of political +discourse. *Humanities and Social Sciences Communications*, *10*(1), +1–11. + +
+
-141\. Ličková, V. (2023). *Coal framing in the Indian political +148\. Ličková, V. (2023). *Coal framing in the Indian political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1327,7 +1392,7 @@ Studies\].
-142\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in +149\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in the acceleration phase of the European energy transition. *Environmental Innovation and Societal Transitions*, *40*, 262–282. @@ -1336,7 +1401,7 @@ Innovation and Societal Transitions*, *40*, 262–282.
-143\. Lockhart, C. (2014). *Discourse network analysis of the Northern +150\. Lockhart, C. (2014). *Discourse network analysis of the Northern Gateway Pipeline project: Assessing environmental governance in the joint review panel process* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental @@ -1346,7 +1411,7 @@ Governance Section\].
-144\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & +151\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & Wagner, P. M. (2021). Unity in diversity? When advocacy coalitions and policy beliefs grow trees in South Africa. *Land Use Policy*, *102*, 105283. @@ -1355,7 +1420,7 @@ policy beliefs grow trees in South Africa. *Land Use Policy*, *102*,
-145\. MalkamÀki, A. (2019). *On the human impacts and governance of +152\. MalkamÀki, A. (2019). *On the human impacts and governance of large-scale tree plantations* \[Doctoral Dissertation, University of Helsinki, Faculty of Agriculture; Forestry\]. @@ -1364,7 +1429,7 @@ Helsinki, Faculty of Agriculture; Forestry\].
-146\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & +153\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & YlÀ-Anttila, T. (2023). *Complex coalitions: Political alliances across relational contexts*. arXiv:2308.14422. @@ -1373,7 +1438,7 @@ relational contexts*. arXiv:2308.14422.
-147\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & +154\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & YlĂ€-Anttila, T. (2021). On the acoustics of policy learning: Can co-participation in policy forums break up echo chambers? *Policy Studies Journal*, *49*(2), 431–456. @@ -1382,7 +1447,7 @@ Studies Journal*, *49*(2), 431–456.
-148\. Mardiah, A. N. R. (2018). *Interface between disaster and +155\. Mardiah, A. N. R. (2018). *Interface between disaster and development: Local economic revival through collaborative post-disaster recovery governance and network in Indonesia* \[PhD thesis, University of Leeds, School of Geography\]. @@ -1392,7 +1457,7 @@ of Leeds, School of Geography\].
-149\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward +156\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward integrated and inclusive disaster risk reduction in Indonesia: Review of regulatory frameworks and institutional networks. In R. Djalante, M. Garschagen, F. Thomalla, & R. Shaw (Eds.), *Disaster risk reduction in @@ -1403,7 +1468,7 @@ Indonesia* (pp. 57–84). Springer.
-150\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. +157\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. A., & Hodgson, D. J. (2023). Stakeholder discourse coalitions and polarisation in the hen harrier conservation debate in news media. *People and Nature*, *5*(2), 668–683. @@ -1413,7 +1478,7 @@ polarisation in the hen harrier conservation debate in news media.
-151\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the +158\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the networking structuration processes of the urban resilience concept in Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1), 46–57. @@ -1422,7 +1487,7 @@ Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1),
-152\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing +159\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing transitions through the lens of discourse networks: Coal phase-out in Germany. *Environmental Innovation and Societal Transitions*, *40*, 315–331. @@ -1431,7 +1496,7 @@ Germany. *Environmental Innovation and Societal Transitions*, *40*,
-153\. Mayer, C. D. (2022). *New west tension and threatened species +160\. Mayer, C. D. (2022). *New west tension and threatened species protection: The western Joshua tree conservation debate in the Morongo Basin, california* \[Master's Thesis, California State University, Long Beach, Department of Geography\]. @@ -1441,7 +1506,7 @@ Beach, Department of Geography\].
-154\. McDonald, E. (2019). *Energy security in the age of +161\. McDonald, E. (2019). *Energy security in the age of interconnection: Cyber-threat framing in British political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1450,7 +1515,7 @@ interconnection: Cyber-threat framing in British political discourse*
-155\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of +162\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of floating signifier: Discourses and network analysis in the bioeconomy policy processes in Argentina and Uruguay. *Forest Policy and Economics*, *154*, 103039. @@ -1460,7 +1525,7 @@ Economics*, *154*, 103039.
-156\. Miles, A. (2020). *Changes in social networks and narratives +163\. Miles, A. (2020). *Changes in social networks and narratives associated with Lake Erie water quality management after the 2014 Toledo water crisis* \[Master's Thesis, The Ohio State University, Graduate Program in Environment; Natural Resources\]. @@ -1470,7 +1535,7 @@ Program in Environment; Natural Resources\].
-157\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear +164\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear energy in the European Union and its implications* \[Doctoral Dissertation, Masaryk University, Department of International Relations; European Studies\]. @@ -1479,7 +1544,7 @@ European Studies\].
-158\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes +165\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes diffuse in space – explaining a missed transition in San Diego’s water sector. *Environmental Innovation and Societal Transitions*, *44*, 29–47. @@ -1488,7 +1553,7 @@ sector. *Environmental Innovation and Societal Transitions*, *44*,
-159\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, +166\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, renewable energy, or both? Public opinion on small hydropower plants in Serbia. *Post-Communist Economies*, *34*(5), 684–713. @@ -1497,7 +1562,7 @@ Serbia. *Post-Communist Economies*, *34*(5), 684–713.
-160\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). +167\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). Relational coupling of multiple streams: The case of COVID-19 infections in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374. @@ -1506,7 +1571,7 @@ in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374.
-161\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? +168\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? For whom? Intersectionality in Canada’s feminist international assistance policy. *International Journal*, *75*(3), 329–348. @@ -1515,7 +1580,7 @@ assistance policy. *International Journal*, *75*(3), 329–348.
-162\. Muller, A. (2014). Het meten van discourscoalities met +169\. Muller, A. (2014). Het meten van discourscoalities met discoursnetwerkanalyse: Naar een formele analyse van het politieke vertoog. *Res Publica*, *56*(3), 337–364. @@ -1524,7 +1589,7 @@ vertoog. *Res Publica*, *56*(3), 337–364.
-163\. Muller, A. (2015). Using discourse network analysis to measure +170\. Muller, A. (2015). Using discourse network analysis to measure discourse coalitions: Towards a formal analysis of political discourse. *World Political Science*, *11*(2), 377–404. @@ -1533,7 +1598,7 @@ discourse coalitions: Towards a formal analysis of political discourse.
-164\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big +171\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big tobacco? Discourse network analysis of the cigarette advertising debate and policy in Indonesia. *Journal of Communication Inquiry*. @@ -1542,15 +1607,35 @@ and policy in Indonesia. *Journal of Communication Inquiry*.
-165\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine +172\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS.
+
+ +173\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den +StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. +Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und +politische Konzepte* (pp. 153–177). transcript Verlag. + + +
+ +
+ +174\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere +Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. +Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: +Öffentliche Debatten und politische Konzepte* (pp. 77–98). transcript +Verlag. + +
+
-166\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level +175\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level governance dynamics from a discourse network perspective: The debate over air pollution regulation in Germany. *Environmental Sciences Europe*, *34*(62), 1–18. @@ -1559,7 +1644,7 @@ Europe*, *34*(62), 1–18.
-167\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A +176\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A discourse network analysis of the evolution of the conflict over Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700. @@ -1568,7 +1653,7 @@ Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700.
-168\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate +177\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate action: Comparing the evolution of narratives using the “narrative rate” index. *Review of Policy Research*. @@ -1576,7 +1661,7 @@ index. *Review of Policy Research*.
-169\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die +178\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS. @@ -1584,7 +1669,7 @@ Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS.
-170\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in +179\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in South Korea’s nuclear power policy making: An advocacy coalition framework approach to policy knowledge. *Politics & Policy*, *51*(2), 201–221. @@ -1593,7 +1678,7 @@ framework approach to policy knowledge. *Politics & Policy*, *51*(2),
-171\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames +180\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames of advocacy coalitions in South Korea’s nuclear energy policy. *Review of Policy Research*, *39*(4), 387–410. @@ -1602,7 +1687,7 @@ of Policy Research*, *39*(4), 387–410.
-172\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position +181\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position und Einfluss der Kirchen in der deutschen Debatte um die embryonale Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.), *Religionspolitik und Politik der Religionen in Deutschland* (pp. @@ -1612,7 +1697,7 @@ Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.),
-173\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting +182\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting in emerging economies – a systematic literature review and research agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820. @@ -1621,7 +1706,7 @@ agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820.
-174\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ +183\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ opozice vƯči hlubinnĂ©mu ĂșloĆŸiĆĄti radioaktivnĂ­ch odpadĆŻ v ČeskĂ© republice* \[PhD thesis, Masarykova univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\]. @@ -1630,7 +1715,7 @@ univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\].
-175\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech +184\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech newspaper coverage: A one man show. In D. Tindall, M. C. J. Stoddart, & R. E. Dunlap (Eds.), *Handbook of anti-environmentalism* (pp. 84–106). Edward Elgar Publishing. @@ -1639,7 +1724,7 @@ Edward Elgar Publishing.
-176\. Ohlendorf, N. (2022). *The political economy of energy +185\. Ohlendorf, N. (2022). *The political economy of energy transitions* \[Doctoral Thesis, Technical University of Berlin, FakultĂ€t VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\]. @@ -1648,7 +1733,7 @@ VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\].
-177\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in +186\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in multi-sector transitions – discourse analysis on hydrogen in Germany. *Environmental Innovation and Societal Transitions*, *47*, 100692. @@ -1657,16 +1742,27 @@ multi-sector transitions – discourse analysis on hydrogen in Germany.
-178\. Ohno, T. (2022). Advocacy coalition framework in environmental +187\. Ohno, T. (2022). Advocacy coalition framework in environmental governance studies: Explaining major policy change for a large dam removal in Japan. *International Review of Public Policy*, *4*(1).
+
+ +188\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa +na polĂ­tica de transporte marĂ­timo de cargas no Brasil: Privatização, +descentralização e abertura para o capital estrangeiro. *Caderno CRH – +Revista de CiĂȘncias Sociais Do Centro de Estudos Pesquisas e Humanidades +Da Universidade Federal Da Bahia*, *36*(2), 1–21. + + +
+
-179\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of +189\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of oil and gas governance in Ghana* \[PhD thesis, University of Colorado Denver, School of Public Affairs\]. @@ -1675,7 +1771,7 @@ Denver, School of Public Affairs\].
-180\. Osei-Kojo, A. (2023). Analysing the stability of advocacy +190\. Osei-Kojo, A. (2023). Analysing the stability of advocacy coalitions and policy frames in Ghana’s oil and gas governance. *Policy & Politics*, *51*(1), 71--90. @@ -1684,7 +1780,7 @@ coalitions and policy frames in Ghana’s oil and gas governance. *Policy
-181\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, +191\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, B. (2018). Natural gas market integration in the Visegrad 4 region: An example to follow or to avoid? *Energy Policy*, *112*, 184–197. @@ -1693,7 +1789,7 @@ example to follow or to avoid? *Energy Policy*, *112*, 184–197.
-182\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & +192\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & Kuhn, J. (2019). Who sides with whom? Towards computational construction of discourse networks for political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics*, @@ -1703,7 +1799,7 @@ Annual Meeting of the Association for Computational Linguistics*,
-183\. Palladino, N. (2021). The role of epistemic communities in the +193\. Palladino, N. (2021). The role of epistemic communities in the “constitutionalization” of internet governance: The example of the European Commission high-level expert group on artificial intelligence. *Telecommunications Policy*, *45*(6), 102149. @@ -1713,7 +1809,7 @@ European Commission high-level expert group on artificial intelligence.
-184\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, +194\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, P. (2020). Interactions among sustainable development goals: Knowledge for identifying multipliers and virtuous cycles. *Sustainable Development*, *28*(5), 1236–1250. @@ -1722,7 +1818,7 @@ Development*, *28*(5), 1236–1250.
-185\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des +195\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des Ă©chelles de la sĂ©curitĂ© dans une rĂ©gion Ă  gĂ©omĂ©trie variable* \[PhD thesis, UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\]. @@ -1732,7 +1828,7 @@ UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\].
-186\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and +196\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and climate change. A complex reflexive systems approach to energy transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.), *The routledge handbook of ideology and international relations* (pp. @@ -1742,7 +1838,7 @@ transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.),
-187\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing +197\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing psychedelics: The segregation of spiritual and scientific narratives within the r/microdosing online community. *Journal of Psychedelic Studies*, *7*(2), 119–128. @@ -1751,34 +1847,44 @@ Studies*, *7*(2), 119–128.
-188\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse +198\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse network analysis of French hydrogen politics* \[Master's Thesis, Masaryk University, Faculty of Social Studies\].
-
+
-189\. Pratama, B. I., & Illahi Ulfa, A. A. (n.d.). Discourse networking +199\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking analysis as alternative research method in communication science studies – discourse networking analysis sebagai metode penelitian alternatif dalam kajian ilmu komunikasi. *Jurnal Penelitian Komunikasi Dan Opini -Publik*, *21*(2), 223278. +Publik*, *21*(2), 126–136.
-190\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). +200\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). Discourse network analysis pada stakeholder dan integrated value creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*, *7*(2), 256–274.
+
+ +201\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., +Espressivo, A., Herlinda, O., Meilissa, Y., & Saminarsih, D. S. (2023). +The advocacy coalition of sugar-sweetened beverage taxes in Indonesia. +*BMJ Global Health*, *8*(Suppl 8), 1–13. + + +
+
-191\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder +202\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder environmental governance in action: REDD+ discourse coalitions in Tanzania. *Ecology and Society*, *19*(2), 66–76. @@ -1787,7 +1893,7 @@ Tanzania. *Ecology and Society*, *19*(2), 66–76.
-192\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education +203\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education policy discourse: Philanthropic funders as entrepreneurs in policy networks. *Interest Groups & Advocacy*, *7*(3), 258–288. @@ -1796,7 +1902,7 @@ networks. *Interest Groups & Advocacy*, *7*(3), 258–288.
-193\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the +204\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the political economy of knowledge production shapes education policy: The case of teacher evaluation in federal policy discourse. *Educational Evaluation and Policy Analysis*, *43*(3), 472–494. @@ -1806,7 +1912,7 @@ Evaluation and Policy Analysis*, *43*(3), 472–494.
-194\. Rennkamp, B. (2019). Power, coalitions and institutional change in +205\. Rennkamp, B. (2019). Power, coalitions and institutional change in South African climate policy. *Climate Policy*, *19*(6), 756–770. @@ -1814,7 +1920,7 @@ South African climate policy. *Climate Policy*, *19*(6), 756–770.
-195\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. +206\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. (2017). Competing coalitions: The politics of renewable energy and fossil fuels in Mexico, South Africa and Thailand. *Energy Research & Social Science*, *34*, 214–223. @@ -1824,7 +1930,7 @@ Social Science*, *34*, 214–223.
-196\. Rinscheid, A. (2018). *Behavioral and institutionalist +207\. Rinscheid, A. (2018). *Behavioral and institutionalist perspectives on preference formation in a contested political context: The case of divesting from nuclear power* \[Doctoral Dissertation, University of St. Gallen, School of Management, Economics, Law, Social @@ -1835,7 +1941,7 @@ Sciences; International Affairs\].
-197\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy +208\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy change: Exploring the role of subsystem polarization in nuclear energy policymaking. *European Policy Analysis*, *1*(2), 34–70. @@ -1844,7 +1950,7 @@ policymaking. *European Policy Analysis*, *1*(2), 34–70.
-198\. Rinscheid, A. (2020). Business power in noisy politics: An +209\. Rinscheid, A. (2020). Business power in noisy politics: An exploration based on discourse network analysis and survey data. *Politics and Governance*, *8*(2), 286–297. @@ -1853,7 +1959,7 @@ exploration based on discourse network analysis and survey data.
-199\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. +210\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. (2020). Why do junctures become critical? Political discourse, agency, and joint belief shifts in comparative perspective. *Regulation & Governance*, *14*(4), 653–673. @@ -1862,7 +1968,7 @@ Governance*, *14*(4), 653–673.
-200\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The +211\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The diffusion of protest against the anti-counterfeiting trade agreement in the age of austerity* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -1872,7 +1978,7 @@ Department of Political; Social Sciences\].
-201\. Rone, J. (2023). Instrumentalising sovereignty claims in British +212\. Rone, J. (2023). Instrumentalising sovereignty claims in British pro- and anti-Brexit mobilisations. *The British Journal of Politics and International Relations*, *25*(3), 444–461. @@ -1881,7 +1987,7 @@ International Relations*, *25*(3), 444–461.
-202\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff +213\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff increase policy in discourse analysis network. *Jurnal Komunikasi*, *17*(1), 62–75. @@ -1889,16 +1995,26 @@ increase policy in discourse analysis network. *Jurnal Komunikasi*,
-203\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, +214\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, right now: Storylines and social identities in coalition building in a local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247.
+
+ +215\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, +R. R. (2023). Bias media mengenai pemberitaan batalnya Indonesia menjadi +tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co. +*Jurnal Ilmiah Wahana Pendidikan*, *9*(23), 547–556. + + +
+
-204\. Schaub, S. (2021). *The politics of water protection* \[Doctoral +216\. Schaub, S. (2021). *The politics of water protection* \[Doctoral Dissertation, University of Heidelberg, Faculty of Economics; Social Studies, Institute of Political Science\]. @@ -1907,7 +2023,7 @@ Studies, Institute of Political Science\].
-205\. Schaub, S. (2021). Public contestation over agricultural +217\. Schaub, S. (2021). Public contestation over agricultural pollution: A discourse network analysis on narrative strategies in the policy process. *Policy Sciences*, *54*(4), 783–821. @@ -1916,7 +2032,7 @@ policy process. *Policy Sciences*, *54*(4), 783–821.
-206\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable +218\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable pharmacy? The influence of public debates on policy responses to pharmaceutical contaminants in water. *Environmental Sciences Europe*, *32*(1), 1–19. @@ -1925,7 +2041,7 @@ pharmaceutical contaminants in water. *Environmental Sciences Europe*,
-207\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy +219\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy network approaches: Evidence from water policy on micropollutants. *Politics and Governance*, *8*(2), 184–199. @@ -1934,7 +2050,7 @@ network approaches: Evidence from water policy on micropollutants.
-208\. Schmid, N. (2020). *The politics of technological change – case +220\. Schmid, N. (2020). *The politics of technological change – case studies from the energy sector* \[Doctoral Thesis, ETH ZĂŒrich, Department of Humanities, Social; Political Sciences, Energy; Technology Policy Group\]. @@ -1943,7 +2059,7 @@ Policy Group\].
-209\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining +221\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining advocacy coalition change with policy feedback. *Policy Studies Journal*, *48*(4), 1109–1134. @@ -1951,7 +2067,7 @@ Journal*, *48*(4), 1109–1134.
-210\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A +222\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A discourse network analysis of German parliamentary debates* \[Diploma Thesis, Charles University, Department of Security Studies\]. @@ -1960,7 +2076,7 @@ Thesis, Charles University, Department of Security Studies\].
-211\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, +223\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, partisanship and paradigmatic change in energy policy – analyzing parliamentary discourse in Germany over 30 years. *Climate Policy*, *19*(6), 771–786. @@ -1969,7 +2085,7 @@ parliamentary discourse in Germany over 30 years. *Climate Policy*,
-212\. Schmitz, L. (2018). *From coherence to coheritization. Explaining +224\. Schmitz, L. (2018). *From coherence to coheritization. Explaining the rise of policy coherence in EU external policy* \[Master's Thesis, Radboud University Nijmegen, Faculteit der Managementwetenschappen\]. @@ -1978,7 +2094,7 @@ Radboud University Nijmegen, Faculteit der Managementwetenschappen\].
-213\. Schmitz, L., & Eimer, T. R. (2020). From coherence to +225\. Schmitz, L., & Eimer, T. R. (2020). From coherence to coheritization: Explaining the rise of policy coherence in EU external policy. *Globalizations*, *17*(4), 629–647. @@ -1987,16 +2103,25 @@ policy. *Globalizations*, *17*(4), 629–647.
-214\. Schmitz, L., & Seidl, T. (2022). As open as possible, as +226\. Schmitz, L., & Seidl, T. (2022). As open as possible, as autonomous as necessary: Understanding the rise of open strategic autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*, *61*(3), 834–852.
+
+ +227\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of +technology legitimacy: Contending storylines surrounding wind energy in +Austria and Switzerland. *Technological Forecasting and Social Change*, +*198*, 122929. + +
+
-215\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and +228\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and displacements in policy discourse: The climate change issue in Germany 2007–2010. In S. Silvern & S. Young (Eds.), *Environmental change and sustainability* (pp. 157–184). Intech. @@ -2005,7 +2130,7 @@ sustainability* (pp. 157–184). Intech.
-216\. Schulz, C. (2020). Forest conservation through markets? A +229\. Schulz, C. (2020). Forest conservation through markets? A discourse network analysis of the debate on funding mechanisms for REDD+ in Brazil. *Environmental Communication*, *14*(2), 202–218. @@ -2014,7 +2139,7 @@ in Brazil. *Environmental Communication*, *14*(2), 202–218.
-217\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of +230\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of Twitter and newspapers: Lessons learned from the nuclear debate in the 2022 French presidential campaign. *French Politics*, *21*(2), 195–221. @@ -2023,7 +2148,7 @@ Twitter and newspapers: Lessons learned from the nuclear debate in the
-218\. Seidl, T. (2021). *Ideas, politics, and technological change: +231\. Seidl, T. (2021). *Ideas, politics, and technological change: Essays on the comparative political economy of digital capitalism* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2032,7 +2157,7 @@ Sciences\].
-219\. Seidl, T. (2022). The politics of platform capitalism: A case +232\. Seidl, T. (2022). The politics of platform capitalism: A case study on the regulation of Uber in New York. *Regulation & Governance*, *16*(2), 357–374. @@ -2040,7 +2165,7 @@ study on the regulation of Uber in New York. *Regulation & Governance*,
-220\. Selle, L. (2017). What multi-level parliamentary system? +233\. Selle, L. (2017). What multi-level parliamentary system? Parliamentary discourses in EU budgetary negotiations (MFF 2014–2020). In S. Becker, M. W. Bauer, & A. De Feo (Eds.), *The new politics of the European Union budget* (pp. 149–172). Nomos. @@ -2050,15 +2175,15 @@ European Union budget* (pp. 149–172). Nomos.
-221\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z -energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis\]. - +234\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z +energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis, Masaryk +University, Faculty of Social Studies\].
-222\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris +235\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris India: Unpacking consensus and conflict through storylines and discourse coalitions. *Energy Research & Social Science*, *91*, 102687. @@ -2067,7 +2192,7 @@ coalitions. *Energy Research & Social Science*, *91*, 102687.
-223\. Shukla, R., & Swarnakar, P. (2022). Energy transition and +236\. Shukla, R., & Swarnakar, P. (2022). Energy transition and dialectics: Tracing discursive resistance to coal through discourse coalition in India. *Globalizations*. @@ -2076,7 +2201,7 @@ coalition in India. *Globalizations*.
-224\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi +237\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi virus corona dengan discourse network analysis. *Jurnal Kebijakan Kesehatan Indonesia: JKKI*, *9*(2), 98–106. @@ -2085,7 +2210,7 @@ Kesehatan Indonesia: JKKI*, *9*(2), 98–106.
-225\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying +238\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying influence on EU efforts to regulate CO2 emissions of cars using network analysis. *Interest Groups & Advocacy*. @@ -2094,7 +2219,7 @@ analysis. *Interest Groups & Advocacy*.
-226\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan +239\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 34–49. @@ -2102,7 +2227,7 @@ UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2),
-227\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada +240\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina. *Kommunikatif*, *12*(1), 37–50. @@ -2110,7 +2235,7 @@ kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina.
-228\. Sohn, C. (2023). The impact of rebordering on cross-border +241\. Sohn, C. (2023). The impact of rebordering on cross-border cooperation actors’ discourses in the Öresund region. A semantic network approach. *Geografiska Annaler: Series B, Human Geography*. @@ -2119,7 +2244,7 @@ approach. *Geografiska Annaler: Series B, Human Geography*.
-229\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu +242\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*, *7*(2), 130–145. @@ -2127,7 +2252,7 @@ ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*,
-230\. Stancioff, C. E. (2016). Locality and landscape change: Cultural +243\. Stancioff, C. E. (2016). Locality and landscape change: Cultural values and social-ecological resiliency in the Kalinago territory. In T. Collins, G. Kindermann, C. Newman, & N. Cronin (Eds.), *Landscape values: Place and praxis. Conference, galway, 29th june–2nd july, 2016* @@ -2138,7 +2263,7 @@ values: Place and praxis. Conference, galway, 29th june–2nd july, 2016*
-231\. Starke, J. (2016). *Generating policy change in situations of +244\. Starke, J. (2016). *Generating policy change in situations of equilibrium: Shifting discourse networks in the case of wild circus animals in Germany* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance @@ -2148,7 +2273,7 @@ Section\].
-232\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. +245\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. J., & Termeer, K. J. A. M. (2023). “Green future” versus “planetary boundaries”? Evolving online discourse coalitions in European bioeconomy conflicts. *Journal of Cleaner Production*, *425*, 139058. @@ -2158,7 +2283,7 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
-233\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +246\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2166,7 +2291,7 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-234\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +247\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\]. @@ -2175,7 +2300,7 @@ of Governance, Master of Public Policy\].
-235\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +248\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2184,7 +2309,7 @@ co-exist?* Palgrave Macmillan.
-236\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +249\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2194,7 +2319,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-237\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +250\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2204,7 +2329,7 @@ alignments through discourse networks and intersectoral relationships.
-238\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +251\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2213,7 +2338,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-239\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +252\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2222,7 +2347,7 @@ Sociologie*, *53*(3), 316–336.
-240\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +253\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2231,7 +2356,7 @@ and the cultural dynamics of multilevel climate governance.
-241\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +254\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2241,7 +2366,7 @@ climate change: Canadian national news and mediated policy networks.
-242\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +255\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy Research*. @@ -2250,7 +2375,7 @@ Research*.
-243\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +256\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2259,7 +2384,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-244\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +257\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2268,7 +2393,7 @@ Economics and Environment*, *83*(4), 62–75.
-245\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +258\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2277,7 +2402,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-246\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +259\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2287,7 +2412,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-247\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +260\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2295,7 +2420,7 @@ thesis, Utrecht University\].
-248\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +261\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2304,7 +2429,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-249\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +262\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2313,7 +2438,7 @@ International Conference on Business and Public Administration (AICoBPA
-250\. Taranger, K. K. (2020). *The institutionalisation of climate +263\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2322,7 +2447,7 @@ Environmental Governance Section\].
-251\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +264\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2331,7 +2456,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-252\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +265\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2341,7 +2466,7 @@ Glasgow, School of Social; Political Sciences\].
-253\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +266\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates on hydraulic fracturing. Comparing coalition politics in north america @@ -2352,7 +2477,7 @@ and europe* (pp. 177–200). Palgrave Macmillan.
-254\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +267\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2361,7 +2486,7 @@ of Policy Research*, *34*(3), 310–330.
-255\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +268\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2370,7 +2495,7 @@ tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu*
-256\. Tuinenburg, J. (2019). *The effect of discourse networks on the +269\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2379,7 +2504,7 @@ Governance\].
-257\. Umansky Casapa, N. (2022). *Securitization and social media +270\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2388,7 +2513,7 @@ Dublin, School of Politics; International Relations\].
-258\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +271\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2396,7 +2521,7 @@ Social Studies\].
-259\. Vaughan, M. (2020). Talking about tax: The discursive distance +272\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2405,7 +2530,7 @@ Politics*, *17*(2), 114–129.
-260\. Vedres, B. (2022). Multivocality and robust action dynamics in +273\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2413,7 +2538,7 @@ political discourse. *Poetics*, *90*, 101576.
-261\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +274\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2422,7 +2547,7 @@ Environmental Change*, *63*, 102117.
-262\. Vogeler, C. S. (2022). The integration of environmental objectives +275\. Vogeler, C. S. (2022). The integration of environmental objectives in the common agricultural policy—partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2431,7 +2556,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-263\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +276\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2440,7 +2565,7 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-264\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +277\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343. @@ -2449,7 +2574,7 @@ the European Parliament. *European Policy Analysis*, *7*, 324–343.
-265\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +278\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2460,7 +2585,7 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
-266\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +279\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2469,7 +2594,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-267\. Wallaschek, S. (2019). The discursive appeal to solidarity and +280\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2477,7 +2602,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-268\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +281\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2487,7 +2612,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-269\. Wallaschek, S. (2020). Analyzing the European parliamentary +282\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2497,7 +2622,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-270\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +283\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2506,7 +2631,7 @@ European Public Policy*, *27*(7), 1034–1053.
-271\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +284\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2515,7 +2640,7 @@ Economy*, *25*(2), 231–247.
-272\. Wallaschek, S. (2020). The discursive construction of solidarity: +285\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2523,7 +2648,7 @@ Studies*, *68*(1), 74–92.
-273\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +286\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2531,7 +2656,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-274\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +287\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2541,7 +2666,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-275\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +288\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2550,7 +2675,7 @@ the public sphere: A discourse network analysis of German newspapers
-276\. Wang, S. (2018). *Dynamic constructed climate change discourses +289\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2560,7 +2685,7 @@ Politics\].
-277\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +290\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2569,7 +2694,7 @@ Public Policy*, *44*(3), 354–368.
-278\. Wang, Y. (2021). Examining the actor coalitions and discourse +291\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2578,7 +2703,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-279\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +292\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2587,7 +2712,7 @@ Policy Analysis Archives*, *25*(34), 1–27.
-280\. Wang, Y. (2020). Understanding Congressional coalitions: A +293\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30. @@ -2596,7 +2721,7 @@ Student Succeeds act. *Education Policy Analysis Archives*, *28*(119),
-281\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +294\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & Hekkert, M. P. (2023). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. *Environmental Policy and Governance*. @@ -2606,7 +2731,7 @@ interest organizations in fragmented socio-technical systems.
-282\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +295\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2615,7 +2740,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-283\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +296\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2625,7 +2750,7 @@ The influence of global and national institutional dynamics.
-284\. Wu, J., & Liu, Y. (2020). Deception detection methods +297\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2634,7 +2759,7 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-285\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +298\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948. @@ -2643,7 +2768,7 @@ Information Technology*, *34*(9), 936–948.
-286\. Yan, K., Wu, H., Bu, K., & Wu, L. (2023). The college admission +299\. Yan, K., Wu, H., Bu, K., & Wu, L. (2023). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. *Higher Education Policy*. @@ -2651,7 +2776,7 @@ policy evolution from 2003 to 2020 in China – a social network analysis.
-287\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +300\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -2660,7 +2785,7 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-288\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +301\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32. @@ -2669,7 +2794,7 @@ infrastructure. *Politics & Policy*, *50*(1), 8–32.
-289\. Yordy, J., DurnovĂĄ, A., & Weible, C. M. (2023). Exploring +302\. Yordy, J., DurnovĂĄ, A., & Weible, C. M. (2023). Exploring emotional discourses: The case of COVID-19 protests in the US media. *Administrative Theory & Praxis*. @@ -2678,7 +2803,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-290\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +303\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -2687,7 +2812,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-291\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +304\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -2696,7 +2821,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-292\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +305\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), 107--129. @@ -2705,7 +2830,7 @@ transmission line sitings. *Public Policy and Administration*, *38*(1),
-293\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +306\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index 355f7755..63735ca0 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -103,6 +103,13 @@ public class Exporter { private int T, t, backboneSize; private SimulatedAnnealingBackboneResult simulatedAnnealingBackboneResult = null; + // time smoothing + private String kernel = "no"; + + public void setKernelFunction(String kernel) { + this.kernel = kernel; + } + /** *

Create a new Exporter class instance, holding an array list of export * statements (i.e., statements with added document information and a hash @@ -1252,7 +1259,11 @@ public void computeResults() { } else if (networkType.equals("twomode") && timeWindow.equals("no")) { computeTwoModeMatrix(); } else if (!networkType.equals("eventlist") && !timeWindow.equals("no")) { - computeTimeWindowMatrices(); + if (this.kernel.equals("no")) { + computeTimeWindowMatrices(); + } else { + computeKernelSmoothedTimeSlices(); + } } } @@ -1667,6 +1678,161 @@ private Matrix computeTwoModeMatrix(ArrayList processedStatemen return matrix; } + public void computeKernelSmoothedTimeSlices() { + // initialise variables and constants + Collections.sort(this.filteredStatements); // probably not necessary, but can't hurt to have it + if (this.windowSize % 2 != 0) { // windowSize is the w constant in the paper; only even numbers are acceptable because adding or subtracting w / 2 to or from gamma would not yield integers + this.windowSize = this.windowSize + 1; + } + final int W_HALF = windowSize / 2; + LocalDateTime b = this.startDateTime; // start of statement list + LocalDateTime e = this.stopDateTime; // end of statement list + LocalDateTime gamma = b; // current time while progressing through list of statements + + // create an array list of all time points, already saved in otherwise empty Matrix objects + Exporter.this.matrixResults = new ArrayList(); + if (Exporter.this.kernel.equals("gaussian")) { + if (timeWindow.equals("minutes")) { + gamma = gamma.minusMinutes(1); + while (gamma.isBefore(e.plusMinutes(1))) { + gamma = gamma.plusMinutes(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } else if (timeWindow.equals("hours")) { + gamma = gamma.minusHours(1); + while (gamma.isBefore(e.plusHours(1))) { + gamma = gamma.plusHours(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } else if (timeWindow.equals("days")) { + gamma = gamma.minusDays(1); + while (gamma.isBefore(e.plusDays(1))) { + gamma = gamma.plusDays(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } else if (timeWindow.equals("weeks")) { + gamma = gamma.minusWeeks(1); + while (gamma.isBefore(e.plusWeeks(1))) { + gamma = gamma.plusWeeks(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } else if (timeWindow.equals("months")) { + gamma = gamma.minusMonths(1); + while (gamma.isBefore(e.plusMonths(1))) { + gamma = gamma.plusMonths(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } else if (timeWindow.equals("years")) { + gamma = gamma.minusYears(1); + while (gamma.isBefore(e.plusYears(1))) { + gamma = gamma.plusYears(1); + Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + } + } + } else { + if (timeWindow.equals("minutes")) { + gamma = gamma.minusMinutes(1); + while (gamma.isBefore(e.plusMinutes(1))) { + gamma = gamma.plusMinutes(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusMinutes(W_HALF).isBefore(b) ? b : gamma.minusMinutes(W_HALF), gamma, gamma.plusMinutes(W_HALF).isAfter(e) ? e : gamma.plusMinutes(W_HALF))); + } + } else if (timeWindow.equals("hours")) { + gamma = gamma.minusHours(1); + while (gamma.isBefore(e.plusHours(1))) { + gamma = gamma.plusHours(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusHours(W_HALF).isBefore(b) ? b : gamma.minusHours(W_HALF), gamma, gamma.plusHours(W_HALF).isAfter(e) ? e : gamma.plusHours(W_HALF))); + } + } else if (timeWindow.equals("days")) { + gamma = gamma.minusDays(1); + while (gamma.isBefore(e.plusDays(1))) { + gamma = gamma.plusDays(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusDays(W_HALF).isBefore(b) ? b : gamma.minusDays(W_HALF), gamma, gamma.plusDays(W_HALF).isAfter(e) ? e : gamma.plusDays(W_HALF))); + } + } else if (timeWindow.equals("weeks")) { + gamma = gamma.minusWeeks(1); + while (gamma.isBefore(e.plusWeeks(1))) { + gamma = gamma.plusWeeks(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusWeeks(W_HALF).isBefore(b) ? b : gamma.minusWeeks(W_HALF), gamma, gamma.plusWeeks(W_HALF).isAfter(e) ? e : gamma.plusWeeks(W_HALF))); + } + } else if (timeWindow.equals("months")) { + gamma = gamma.minusMonths(1); + while (gamma.isBefore(e.plusMonths(1))) { + gamma = gamma.plusMonths(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusMonths(W_HALF).isBefore(b) ? b : gamma.minusMonths(W_HALF), gamma, gamma.plusMonths(W_HALF).isAfter(e) ? e : gamma.plusMonths(W_HALF))); + } + } else if (timeWindow.equals("years")) { + gamma = gamma.minusYears(1); + while (gamma.isBefore(e.plusYears(1))) { + gamma = gamma.plusYears(1); + Exporter.this.matrixResults.add(new Matrix(gamma.minusYears(W_HALF).isBefore(b) ? b : gamma.minusYears(W_HALF), gamma, gamma.plusYears(W_HALF).isAfter(e) ? e : gamma.plusYears(W_HALF))); + } + } + } + + // process each matrix result in a parallel stream instead of for-loop and add calculation results + ArrayList processedResults = Exporter.this.matrixResults.parallelStream() + .map(matrixResult -> processTimeSlice(matrixResult, Exporter.this.filteredStatements)) + .collect(Collectors.toCollection(ArrayList::new)); + Exporter.this.matrixResults = processedResults; + } + + /** + * Add results to a matrix result by calculating the appropriate time slice projection and inserting it. + * + * @param matrixResult + */ + private Matrix processTimeSlice(Matrix matrixResult, List statements) { + List filteredStatements = new ArrayList<>(statements); + filteredStatements.removeIf(statement -> statement.getDateTime().isBefore(matrixResult.getStart()) || statement.getDateTime().isAfter(matrixResult.getStop())); + if (this.networkType.equals("twomode")) { + return(computeTwoModeMatrixSmoothed(filteredStatements, matrixResult)); + } else { + return(computeOneModeMatrixSmoothed(filteredStatements, matrixResult)); + } + } + + /** + * Compute a two-mode matrix, potentially with smoothing. + * + * @param statements A filtered list of statements corresponding to the respective time slice. + * @param matrixResult An empty matrix containing only the start, mid, and end dates of the time slice. + * @return A {@link Matrix} object. + */ + private Matrix computeTwoModeMatrixSmoothed(List statements, Matrix matrixResult) { + + /* + if (Exporter.this.kernel.equals("gaussian")) { + + } else { + if (Exporter.this.kernel.equals("uniform")) { + + } else if (Exporter.this.kernel.equals("epanechnikov")) { + + } else if (Exporter.this.kernel.equals("triangular")) { + + } + } + */ + + // TODO + + return matrixResult; + } + + /** + * Compute a one-mode matrix, potentially with smoothing. + * + * @param statements A filtered list of statements corresponding to the respective time slice. + * @param matrixResult An empty matrix containing only the start, mid, and end dates of the time slice. + * @return A {@link Matrix} object. + */ + private Matrix computeOneModeMatrixSmoothed(List statements, Matrix matrixResult) { + + // TODO + + return matrixResult; + } + /** * Create a series of one-mode or two-mode networks using a moving time window. */ diff --git a/dna/src/main/java/export/Matrix.java b/dna/src/main/java/export/Matrix.java index 37d722ec..822a23b3 100644 --- a/dna/src/main/java/export/Matrix.java +++ b/dna/src/main/java/export/Matrix.java @@ -28,6 +28,18 @@ public Matrix(double[][] matrix, String[] rowNames, String[] columnNames, boolea this.stop = stop; } + /** + * Constructor for empty object with only the dates inserted (useful for time slice creation). + * @param start The start date. + * @param dateTime The mid-point. + * @param stop The end date. + */ + public Matrix(LocalDateTime start, LocalDateTime dateTime, LocalDateTime stop) { + this.start = start; + this.dateTime = dateTime; + this.stop = stop; + } + /** * Copy constructor. */ From ae1cdc317e47a20d456aadcc631b2205c052e107 Mon Sep 17 00:00:00 2001 From: TimHenrichsen Date: Thu, 25 Jan 2024 16:03:20 +0000 Subject: [PATCH 05/43] Bugfix in dna_barplot. --- rDNA/rDNA/R/rDNA.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index c29a6ba3..167626f7 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -2229,7 +2229,7 @@ autoplot.dna_barplot <- function(object, if (binary) { # Bars for the binary case g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], color = .data[["text_color"]]), - stat = .data[["identity"]], + stat = "identity", width = barWidth, show.legend = FALSE) # For the integer case with positive and negative values From ef85c0883f276cde0a254a5afc9fc2020e4f9c37 Mon Sep 17 00:00:00 2001 From: leifeld Date: Wed, 31 Jan 2024 01:04:01 +0000 Subject: [PATCH 06/43] Implemented first draft of kernel-smoothed time slices --- dna/src/main/java/dna/HeadlessDna.java | 5 +- dna/src/main/java/export/Exporter.java | 330 ++++++++++++++++++++----- dna/src/main/java/export/Matrix.java | 5 +- 3 files changed, 276 insertions(+), 64 deletions(-) diff --git a/dna/src/main/java/dna/HeadlessDna.java b/dna/src/main/java/dna/HeadlessDna.java index 93d6b37b..3131b29b 100644 --- a/dna/src/main/java/dna/HeadlessDna.java +++ b/dna/src/main/java/dna/HeadlessDna.java @@ -364,6 +364,7 @@ private LocalDateTime[] formatDateTime(String startDate, String startTime, Strin * @param stopTime Stop time for the export, provided as a {@link String} with format {@code "HH:mm:ss"}. * @param timeWindow A {@link String} indicating the time window setting. Valid options are {@code "no"}, {@code "events"}, {@code "seconds"}, {@code "minutes"}, {@code "hours"}, {@code "days"}, {@code "weeks"}, {@code "months"}, and {@code "years"}. * @param windowSize Duration of the time window in the units specified in the {@code timeWindow} argument. + * @param kernel The kernel function for temporal smoothing: {@code "no"}, {@code "uniform"}, {@code "epanechnikov"}, {@code "triangular"}, or {@code "gaussian"}. * @param excludeVariables A {@link String} array with n elements, indicating the variable of the n'th value. * @param excludeValues A {@link String} array with n elements, indicating the value pertaining to the n'th variable {@link String}. * @param excludeAuthors A {@link String} array of values to exclude in the {@code author} variable at the document level. @@ -381,7 +382,7 @@ private LocalDateTime[] formatDateTime(String startDate, String startTime, Strin */ public void rNetwork(String networkType, String statementType, String variable1, boolean variable1Document, String variable2, boolean variable2Document, String qualifier, boolean qualifierDocument, String qualifierAggregation, String normalization, boolean includeIsolates, - String duplicates, String startDate, String stopDate, String startTime, String stopTime, String timeWindow, int windowSize, + String duplicates, String startDate, String stopDate, String startTime, String stopTime, String timeWindow, int windowSize, String kernel, String[] excludeVariables, String[] excludeValues, String[] excludeAuthors, String[] excludeSources, String[] excludeSections, String[] excludeTypes, boolean invertValues, boolean invertAuthors, boolean invertSources, boolean invertSections, boolean invertTypes, String outfile, String fileFormat) { @@ -441,6 +442,7 @@ public void rNetwork(String networkType, String statementType, String variable1, invertTypes, fileFormat, outfile); + this.exporter.setKernelFunction(kernel); // step 2: filter this.exporter.loadData(); @@ -448,6 +450,7 @@ public void rNetwork(String networkType, String statementType, String variable1, // step 3: compute results if (networkType.equals("eventlist")) { + // TODO } else { try { this.exporter.computeResults(); diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index 63735ca0..fbfe4cf8 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -23,7 +23,9 @@ import org.ojalgo.matrix.decomposition.Eigenvalue; import java.io.*; +import java.time.Duration; import java.time.LocalDateTime; +import java.time.Period; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.temporal.WeekFields; @@ -1678,7 +1680,25 @@ private Matrix computeTwoModeMatrix(ArrayList processedStatemen return matrix; } + /** + * Compute a series of network matrices using kernel smoothing. + * + * This function creates a series of network matrices (one-mode or two-mode) similar to the time window approach, + * but using kernel smoothing around a forward-moving mid-point on the time axis (gamma). The networks are defined + * by the mid-point {@code gamma}, the window size {@code w}, and the kernel function. These parameters are saved in + * the fields of the class. All networks have the same dimensions, i.e., isolates are included at any time point, to + * make the networks comparable and amenable to distance functions and other pair-wise computations. + */ public void computeKernelSmoothedTimeSlices() { + // check and fix normalization setting for unimplemented normalization settings + if (Exporter.this.normalization.equals("jaccard") || Exporter.this.normalization.equals("cosine") || Exporter.this.normalization.equals("activity") || Exporter.this.normalization.equals("prominence")) { + LogEvent l = new LogEvent(Logger.WARNING, + Exporter.this.normalization + " normalization not implemented.", + Exporter.this.normalization + " normalization has not been implemented (yet?) for kernel-smoothed networks. Using \"average\" normalization instead."); + Exporter.this.normalization = "average"; + Dna.logger.log(l); + } + // initialise variables and constants Collections.sort(this.filteredStatements); // probably not necessary, but can't hurt to have it if (this.windowSize % 2 != 0) { // windowSize is the w constant in the paper; only even numbers are acceptable because adding or subtracting w / 2 to or from gamma would not yield integers @@ -1689,148 +1709,334 @@ public void computeKernelSmoothedTimeSlices() { LocalDateTime e = this.stopDateTime; // end of statement list LocalDateTime gamma = b; // current time while progressing through list of statements - // create an array list of all time points, already saved in otherwise empty Matrix objects + // save the labels of the variables and qualifier and put indices in hash maps for fast retrieval + String[] var1Values = retrieveValues(Exporter.this.filteredStatements, Exporter.this.variable1, Exporter.this.variable1Document); + String[] var2Values = retrieveValues(Exporter.this.filteredStatements, Exporter.this.variable2, Exporter.this.variable2Document); + String[] qualValues = retrieveValues(Exporter.this.filteredStatements, Exporter.this.qualifier, Exporter.this.qualifierDocument); + if (dataTypes.get(Exporter.this.qualifier).equals("integer")) { + int[] qual = Exporter.this.originalStatements.stream().mapToInt(s -> (int) s.get(Exporter.this.qualifier)).distinct().sorted().toArray(); + if (qual.length < qualValues.length) { + qualValues = IntStream.rangeClosed(qual[0], qual[qual.length - 1]) + .mapToObj(i -> String.valueOf(i)) + .toArray(String[]::new); + } + } + HashMap var1Map = new HashMap<>(); + for (int i = 0; i < var1Values.length; i++) { + var1Map.put(var1Values[i], i); + } + HashMap var2Map = new HashMap<>(); + for (int i = 0; i < var2Values.length; i++) { + var2Map.put(var2Values[i], i); + } + HashMap qualMap = new HashMap<>(); + for (int i = 0; i < qualValues.length; i++) { + qualMap.put(qualValues[i], i); + } + + // create an array list of empty Matrix results, store all date-time stamps in them, and save indices in a hash map Exporter.this.matrixResults = new ArrayList(); - if (Exporter.this.kernel.equals("gaussian")) { + HashMap timeMap = new HashMap<>(); + if (Exporter.this.kernel.equals("gaussian")) { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by the start and end of the whole time range; the actual matrix is injected later if (timeWindow.equals("minutes")) { gamma = gamma.minusMinutes(1); while (gamma.isBefore(e.plusMinutes(1))) { gamma = gamma.plusMinutes(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("hours")) { gamma = gamma.minusHours(1); while (gamma.isBefore(e.plusHours(1))) { gamma = gamma.plusHours(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("days")) { gamma = gamma.minusDays(1); while (gamma.isBefore(e.plusDays(1))) { gamma = gamma.plusDays(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("weeks")) { gamma = gamma.minusWeeks(1); while (gamma.isBefore(e.plusWeeks(1))) { gamma = gamma.plusWeeks(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("months")) { gamma = gamma.minusMonths(1); while (gamma.isBefore(e.plusMonths(1))) { gamma = gamma.plusMonths(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("years")) { gamma = gamma.minusYears(1); while (gamma.isBefore(e.plusYears(1))) { gamma = gamma.plusYears(1); - Exporter.this.matrixResults.add(new Matrix(b, gamma, e)); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } - } else { + } else { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by width w; the actual matrix is injected later if (timeWindow.equals("minutes")) { gamma = gamma.minusMinutes(1); while (gamma.isBefore(e.plusMinutes(1))) { gamma = gamma.plusMinutes(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusMinutes(W_HALF).isBefore(b) ? b : gamma.minusMinutes(W_HALF), gamma, gamma.plusMinutes(W_HALF).isAfter(e) ? e : gamma.plusMinutes(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMinutes(W_HALF).isBefore(b) ? b : gamma.minusMinutes(W_HALF), gamma, gamma.plusMinutes(W_HALF).isAfter(e) ? e : gamma.plusMinutes(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("hours")) { gamma = gamma.minusHours(1); while (gamma.isBefore(e.plusHours(1))) { gamma = gamma.plusHours(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusHours(W_HALF).isBefore(b) ? b : gamma.minusHours(W_HALF), gamma, gamma.plusHours(W_HALF).isAfter(e) ? e : gamma.plusHours(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusHours(W_HALF).isBefore(b) ? b : gamma.minusHours(W_HALF), gamma, gamma.plusHours(W_HALF).isAfter(e) ? e : gamma.plusHours(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("days")) { gamma = gamma.minusDays(1); while (gamma.isBefore(e.plusDays(1))) { gamma = gamma.plusDays(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusDays(W_HALF).isBefore(b) ? b : gamma.minusDays(W_HALF), gamma, gamma.plusDays(W_HALF).isAfter(e) ? e : gamma.plusDays(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusDays(W_HALF).isBefore(b) ? b : gamma.minusDays(W_HALF), gamma, gamma.plusDays(W_HALF).isAfter(e) ? e : gamma.plusDays(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("weeks")) { gamma = gamma.minusWeeks(1); while (gamma.isBefore(e.plusWeeks(1))) { gamma = gamma.plusWeeks(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusWeeks(W_HALF).isBefore(b) ? b : gamma.minusWeeks(W_HALF), gamma, gamma.plusWeeks(W_HALF).isAfter(e) ? e : gamma.plusWeeks(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusWeeks(W_HALF).isBefore(b) ? b : gamma.minusWeeks(W_HALF), gamma, gamma.plusWeeks(W_HALF).isAfter(e) ? e : gamma.plusWeeks(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("months")) { gamma = gamma.minusMonths(1); while (gamma.isBefore(e.plusMonths(1))) { gamma = gamma.plusMonths(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusMonths(W_HALF).isBefore(b) ? b : gamma.minusMonths(W_HALF), gamma, gamma.plusMonths(W_HALF).isAfter(e) ? e : gamma.plusMonths(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMonths(W_HALF).isBefore(b) ? b : gamma.minusMonths(W_HALF), gamma, gamma.plusMonths(W_HALF).isAfter(e) ? e : gamma.plusMonths(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("years")) { gamma = gamma.minusYears(1); while (gamma.isBefore(e.plusYears(1))) { gamma = gamma.plusYears(1); - Exporter.this.matrixResults.add(new Matrix(gamma.minusYears(W_HALF).isBefore(b) ? b : gamma.minusYears(W_HALF), gamma, gamma.plusYears(W_HALF).isAfter(e) ? e : gamma.plusYears(W_HALF))); + Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusYears(W_HALF).isBefore(b) ? b : gamma.minusYears(W_HALF), gamma, gamma.plusYears(W_HALF).isAfter(e) ? e : gamma.plusYears(W_HALF))); + timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } } + // create a 4D array, go through the statements, and populate the array + int[][][][] X = new int[var1Values.length][var2Values.length][qualValues.length][Exporter.this.matrixResults.size()]; // var1 x var2 x qual x time + Exporter.this.filteredStatements.stream().forEach(s -> { + int var1Index = -1; + if (Exporter.this.variable1Document) { + if (Exporter.this.variable1.equals("author")) { + var1Index = var1Map.get(s.getAuthor()); + } else if (Exporter.this.variable1.equals("source")) { + var1Index = var1Map.get(s.getSource()); + } else if (Exporter.this.variable1.equals("section")) { + var1Index = var1Map.get(s.getSection()); + } else if (Exporter.this.variable1.equals("type")) { + var1Index = var1Map.get(s.getType()); + } else if (Exporter.this.variable1.equals("id")) { + var1Index = var1Map.get(s.getDocumentIdAsString()); + } else if (Exporter.this.variable1.equals("title")) { + var1Index = var1Map.get(s.getTitle()); + } + } else { + var1Index = var1Map.get((String) s.get(Exporter.this.variable1)); + } + int var2Index = -1; + if (Exporter.this.variable2Document) { + if (Exporter.this.variable2.equals("author")) { + var2Index = var2Map.get(s.getAuthor()); + } else if (Exporter.this.variable2.equals("source")) { + var2Index = var2Map.get(s.getSource()); + } else if (Exporter.this.variable2.equals("section")) { + var2Index = var2Map.get(s.getSection()); + } else if (Exporter.this.variable2.equals("type")) { + var2Index = var2Map.get(s.getType()); + } else if (Exporter.this.variable2.equals("id")) { + var2Index = var2Map.get(s.getDocumentIdAsString()); + } else if (Exporter.this.variable2.equals("title")) { + var2Index = var2Map.get(s.getTitle()); + } + } else { + var2Index = var2Map.get((String) s.get(Exporter.this.variable2)); + } + int qualIndex = -1; + if (Exporter.this.qualifierDocument) { + if (Exporter.this.qualifier.equals("author")) { + qualIndex = qualMap.get(s.getAuthor()); + } else if (Exporter.this.qualifier.equals("source")) { + qualIndex = qualMap.get(s.getSource()); + } else if (Exporter.this.qualifier.equals("section")) { + qualIndex = qualMap.get(s.getSection()); + } else if (Exporter.this.qualifier.equals("type")) { + qualIndex = qualMap.get(s.getType()); + } else if (Exporter.this.qualifier.equals("id")) { + qualIndex = qualMap.get(s.getDocumentIdAsString()); + } else if (Exporter.this.qualifier.equals("title")) { + qualIndex = qualMap.get(s.getTitle()); + } + } else { + if (dataTypes.get(Exporter.this.qualifier).equals("integer") || dataTypes.get(Exporter.this.qualifier).equals("boolean")) { + qualIndex = qualMap.get(String.valueOf((int) s.get(Exporter.this.qualifier))); + } else { + qualIndex = qualMap.get(s.get(Exporter.this.qualifier)); + } + } + int timeIndex = timeMap.get(s.getDateTime()); + X[var1Index][var2Index][qualIndex][timeIndex]++; + }); + // process each matrix result in a parallel stream instead of for-loop and add calculation results ArrayList processedResults = Exporter.this.matrixResults.parallelStream() - .map(matrixResult -> processTimeSlice(matrixResult, Exporter.this.filteredStatements)) + .map(matrixResult -> processTimeSlice(matrixResult, X, Exporter.this.matrixResults)) .collect(Collectors.toCollection(ArrayList::new)); Exporter.this.matrixResults = processedResults; } /** - * Add results to a matrix result by calculating the appropriate time slice projection and inserting it. + * Compute a one-mode or two-mode network matrix with kernel-weighting and inject it into a {@link Matrix} object. * - * @param matrixResult - */ - private Matrix processTimeSlice(Matrix matrixResult, List statements) { - List filteredStatements = new ArrayList<>(statements); - filteredStatements.removeIf(statement -> statement.getDateTime().isBefore(matrixResult.getStart()) || statement.getDateTime().isAfter(matrixResult.getStop())); - if (this.networkType.equals("twomode")) { - return(computeTwoModeMatrixSmoothed(filteredStatements, matrixResult)); - } else { - return(computeOneModeMatrixSmoothed(filteredStatements, matrixResult)); - } - } - - /** - * Compute a two-mode matrix, potentially with smoothing. + * To compute the kernel-weighted network projection, the 4D array X is needed because it stores the statement data, + * the current matrix result is needed because it stores the mid-point gamma, and the array list of all matrix + * results is needed because the mid-points of the matrices contain the time stamps for the time dimension in X, + * which is required to establish the time difference between the current matrix mid-point and the respective + * statement in X. * - * @param statements A filtered list of statements corresponding to the respective time slice. - * @param matrixResult An empty matrix containing only the start, mid, and end dates of the time slice. - * @return A {@link Matrix} object. + * @param matrixResult The matrix result into which the network matrix will be inserted. + * @param X A 4D array containing the data. + * @param matrixResults All matrix results, containing the temporal information for the fourth dimension of X. + * @return The matrix result after inserting the network matrix. */ - private Matrix computeTwoModeMatrixSmoothed(List statements, Matrix matrixResult) { - - /* - if (Exporter.this.kernel.equals("gaussian")) { - - } else { - if (Exporter.this.kernel.equals("uniform")) { - - } else if (Exporter.this.kernel.equals("epanechnikov")) { - - } else if (Exporter.this.kernel.equals("triangular")) { - + private Matrix processTimeSlice(Matrix matrixResult, int[][][][] X, ArrayList matrixResults) { + if (this.networkType.equals("twomode")) { + double[][] m = new double[X.length][X[0].length]; + for (int i = 0; i < X.length; i++) { + for (int j = 0; j < X[0].length; j++) { + for (int k = 0; k < X[0][0].length; k++) { + for (int t = 0; t < X[0][0][0].length; t++) { + m[i][j] = k * X[i][j][k][t] * zeta(matrixResults.get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } + } + } } + } else if (this.networkType.equals("onemode")) { + double[][] m = new double[X.length][X.length]; + double[][] norm = new double[X.length][X.length]; + for (int i = 0; i < X.length; i++) { + for (int i2 = 0; i2 < X.length; i2++) { + for (int j = 0; j < X[0].length; j++) { + for (int k = 0; k < X[0][0].length; k++) { + for (int t = 0; t < X[0][0][0].length; t++) { + if (Exporter.this.normalization.equals("average")) { + norm[i][i2] = norm[i][i2] + 2.0 / (X[i][j][k][t] + X[i2][j][k][t]); + } + for (int k2 = 0; k2 < X[0][0].length; k2++) { + for (int t2 = 0; t2 < X[0][0][0].length; t2++) { + double cong = Math.sqrt(X[i][j][k][t] * X[i2][j][k2][t2]); + double qsim = 1.0; + if (!dataTypes.get(Exporter.this.qualifier).equals("short text") && !Exporter.this.qualifierDocument) { + qsim = Math.abs(1.0 - ((double) Math.abs(k - k2) / (double) Math.abs(X[0][0].length - 1))); + } + double qdiff = 1.0 - qsim; + double zeta = Math.sqrt(zeta(matrixResults.get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel) * zeta(matrixResults.get(t2).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel)); + if (Exporter.this.qualifierAggregation.equals("congruence")) { + m[i][i2] = cong * qsim * zeta; + } else if (Exporter.this.qualifierAggregation.equals("conflict")) { + m[i][i2] = cong * qdiff * zeta; + } else if (Exporter.this.qualifierAggregation.equals("subtract")) { + m[i][i2] = cong * qsim * zeta - cong * qdiff * zeta; + } else if (Exporter.this.qualifierAggregation.equals("ignore")) { + m[i][i2] = cong * zeta; + } + } + } + } + } + } + } + } + if (Exporter.this.normalization.equals("average")) { + for (int i = 0; i < X.length; i++) { + for (int i2 = 0; i2 < X.length; i2++) { + m[i][i2] = m[i][i2] * norm[i][i2]; + } + } + } + matrixResult.setMatrix(m); } - */ - - // TODO - return matrixResult; } - /** - * Compute a one-mode matrix, potentially with smoothing. - * - * @param statements A filtered list of statements corresponding to the respective time slice. - * @param matrixResult An empty matrix containing only the start, mid, and end dates of the time slice. - * @return A {@link Matrix} object. - */ - private Matrix computeOneModeMatrixSmoothed(List statements, Matrix matrixResult) { - - // TODO - - return matrixResult; + /** + * Return a standardized time weight after applying a kernel function to a time difference. + * + * @param t The current time in the time window. + * @param gamma The mid-point of the time window. + * @param w The width of the time window, which defines the beginning and end of the time window. + * @param timeWindow The time unit. Valid values are {@code "seconds"}, {@code "minutes"}, {@code "hours"}, {@code "days"}, {@code "weeks"}, {@code "months"}, and {@code "years"}. + * @param kernel The kernel function ({@code "uniform"}, {@code "epanechnikov"}, {@code "triangular"}, or {@code "gaussian"}). + * @return Kernel-weighted time difference between time points t and gamma. + */ + private double zeta(LocalDateTime t, LocalDateTime gamma, int w, String timeWindow, String kernel) { + Duration duration = Duration.between(t, gamma); + Period period; + long diff = 0; + switch (timeWindow) { + case "seconds": + diff = duration.toSeconds(); + break; + case "minutes": + diff = duration.toMinutes(); + break; + case "hours": + diff = duration.toHours(); + break; + case "days": + diff = duration.toDays(); + break; + case "weeks": + diff = duration.toDays() / 7; + break; + case "months": + period = Period.between(t.toLocalDate(), gamma.toLocalDate()); + diff = period.getMonths() + (long) period.getYears() * (long) 12; + break; + case "years": + period = Period.between(t.toLocalDate(), gamma.toLocalDate()); + diff = period.getYears(); + break; + } + + double diff_std = 2 * (double) diff / (double) w; // standardised time difference between -1 and 1 + + if (kernel.equals("uniform")) { + if (diff_std >= -1 && diff_std <= 1) { + return 0.5; + } else { + return 0.0; + } + } else if (kernel.equals("epanechnikov")) { + if (diff_std >= -1 && diff_std <= 1) { + return 0.75 * (1.0 - diff_std) * (1.0 - diff_std); + } else { + return 0.0; + } + } else if (kernel.equals("triangular")) { + if (diff_std >= -1 && diff_std <= 1) { + return Math.abs(1.0 - diff_std); + } else { + return 0.0; + } + } else if (kernel.equals("gaussian")) { + return (1.0 / Math.sqrt(2.0 * Math.PI)) * Math.exp(-0.5 * diff_std * diff_std); + } + return 0.0; } /** diff --git a/dna/src/main/java/export/Matrix.java b/dna/src/main/java/export/Matrix.java index 822a23b3..fb30a06e 100644 --- a/dna/src/main/java/export/Matrix.java +++ b/dna/src/main/java/export/Matrix.java @@ -34,7 +34,10 @@ public Matrix(double[][] matrix, String[] rowNames, String[] columnNames, boolea * @param dateTime The mid-point. * @param stop The end date. */ - public Matrix(LocalDateTime start, LocalDateTime dateTime, LocalDateTime stop) { + public Matrix(String[] rowNames, String[] columnNames, boolean integer, LocalDateTime start, LocalDateTime dateTime, LocalDateTime stop) { + this.rowNames = rowNames; + this.columnNames = columnNames; + this.integer = integer; this.start = start; this.dateTime = dateTime; this.stop = stop; From 38eac001e7b4177c78bdcd002527f98c172c92cd Mon Sep 17 00:00:00 2001 From: leifeld Date: Sun, 4 Feb 2024 01:27:28 +0000 Subject: [PATCH 07/43] Kernel smoothing and better parallelization for phase transitions --- bibliography/build.gradle | 52 +-- build/bibliography.md | 2 +- dna/build.gradle | 4 + dna/src/main/java/dna/Dna.java | 2 +- dna/src/main/java/export/Exporter.java | 300 +++++++++++------ rDNA/build.gradle | 25 +- rDNA/rDNA/DESCRIPTION | 2 +- rDNA/rDNA/NAMESPACE | 2 + rDNA/rDNA/R/rDNA.R | 425 ++++++++++++++++++++++++- rDNA/rDNA/man/dna_getHeadlessDna.Rd | 38 +++ rDNA/rDNA/man/dna_init.Rd | 1 + rDNA/rDNA/man/dna_jar.Rd | 1 + rDNA/rDNA/man/dna_network.Rd | 14 + rDNA/rDNA/man/dna_phaseTransitions.Rd | 83 ++++- rDNA/rDNA/man/dna_sample.Rd | 1 + 15 files changed, 791 insertions(+), 161 deletions(-) create mode 100644 rDNA/rDNA/man/dna_getHeadlessDna.Rd diff --git a/bibliography/build.gradle b/bibliography/build.gradle index 494a3bc9..421680e1 100644 --- a/bibliography/build.gradle +++ b/bibliography/build.gradle @@ -1,9 +1,7 @@ task bibliographyMarkdown { + inputs.dir '.' doLast { - exec { - workingDir '.' - commandLine 'mkdir', "-p", "$rootDir/build" - } + mkdir "$rootDir/build" exec { workingDir '.' commandLine 'pandoc', "-t", "gfm", "-s", "--csl", "apa-numeric-superscript-brackets.csl", "--citeproc", "-o", "$rootDir/build/bibliography.md", "bibliography.tex" @@ -12,59 +10,35 @@ task bibliographyMarkdown { } task bibliographyPdflatex { + inputs.dir '.' doLast{ - exec { - workingDir '.' - commandLine 'mkdir', "-p", "temp" - } - exec { - workingDir '.' - commandLine 'mkdir', "-p", "$rootDir/build" + mkdir "./temp" + mkdir "$rootDir/build" + copy { + from 'bibliography.tex', 'bibliography.bib' + into 'temp' } - exec { - workingDir '.' - commandLine 'cp', "bibliography.tex", "temp/" - } - exec { - workingDir '.' - commandLine 'cp', "bibliography.bib", "temp/" - } - } - doLast { exec { workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" - } - exec { - workingDir 'temp' commandLine 'bibtex', "bibliography" - } - exec { - workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" - } - exec { - workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" } - } - doLast { - exec { - workingDir '.' - commandLine 'mv', "temp/bibliography.pdf", "$rootDir/build/" - } - exec { - workingDir '.' - commandLine 'rm', "-r", "temp" + copy { + from 'temp/bibliography.pdf' + into "$rootDir/build/" } + delete 'temp' } } task build { dependsOn bibliographyMarkdown dependsOn bibliographyPdflatex + inputs.dir '.' def outputDir = file("$rootDir/build/") outputs.dir outputDir diff --git a/build/bibliography.md b/build/bibliography.md index 9c34e7be..b500ceb6 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-01-10 +date: 2024-02-04 title: "Discourse Network Analysis: Bibliography" --- diff --git a/dna/build.gradle b/dna/build.gradle index 85d425be..b4d6d65d 100644 --- a/dna/build.gradle +++ b/dna/build.gradle @@ -5,6 +5,8 @@ plugins { // create jar file, not just .class files jar { + inputs.dir '.' + // point the manifest to the right class manifest { attributes 'Main-Class': 'dna.Dna' @@ -77,4 +79,6 @@ dependencies { implementation group: 'me.tongfei', name: 'progressbar', version: '0.9.4' // https://mvnrepository.com/artifact/org.ojalgo/ojalgo implementation group: 'org.ojalgo', name: 'ojalgo', version: '51.4.1' + // https://mvnrepository.com/artifact/org.apache.commons/commons-math3 + implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1' } diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 5dc60244..47532b3a 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-01-01"; + public static final String date = "2024-02-04"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index fbfe4cf8..675037d9 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -10,6 +10,9 @@ import logger.Logger; import me.tongfei.progressbar.ProgressBar; import model.*; +import org.apache.commons.math3.linear.EigenDecomposition; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.linear.Array2DRowRealMatrix; import org.jdom.Attribute; import org.jdom.Comment; import org.jdom.Element; @@ -883,7 +886,7 @@ String[] extractLabels( * {@link #filteredStatements} slot of the class. */ public void filterStatements() { - try (ProgressBar pb = new ProgressBar("Filtering statements...", this.originalStatements.size())) { + try (ProgressBar pb = new ProgressBar("Filtering statements", this.originalStatements.size())) { pb.stepTo(0); // create a deep copy of the original statements @@ -1682,7 +1685,6 @@ private Matrix computeTwoModeMatrix(ArrayList processedStatemen /** * Compute a series of network matrices using kernel smoothing. - * * This function creates a series of network matrices (one-mode or two-mode) similar to the time window approach, * but using kernel smoothing around a forward-moving mid-point on the time axis (gamma). The networks are defined * by the mid-point {@code gamma}, the window size {@code w}, and the kernel function. These parameters are saved in @@ -1699,6 +1701,15 @@ public void computeKernelSmoothedTimeSlices() { Dna.logger.log(l); } + // check and fix two-mode qualifier aggregation for unimplemented settings + if (Exporter.this.qualifierAggregation.equals("combine")) { + LogEvent l = new LogEvent(Logger.WARNING, + Exporter.this.qualifierAggregation + " qualifier aggregation not implemented.", + Exporter.this.qualifierAggregation + " qualifier aggregation has not been implemented for kernel-smoothed networks. Using \"subtract\" qualifier aggregation instead."); + Exporter.this.qualifierAggregation = "subtract"; + Dna.logger.log(l); + } + // initialise variables and constants Collections.sort(this.filteredStatements); // probably not necessary, but can't hurt to have it if (this.windowSize % 2 != 0) { // windowSize is the w constant in the paper; only even numbers are acceptable because adding or subtracting w / 2 to or from gamma would not yield integers @@ -1710,14 +1721,14 @@ public void computeKernelSmoothedTimeSlices() { LocalDateTime gamma = b; // current time while progressing through list of statements // save the labels of the variables and qualifier and put indices in hash maps for fast retrieval - String[] var1Values = retrieveValues(Exporter.this.filteredStatements, Exporter.this.variable1, Exporter.this.variable1Document); - String[] var2Values = retrieveValues(Exporter.this.filteredStatements, Exporter.this.variable2, Exporter.this.variable2Document); - String[] qualValues = retrieveValues(Exporter.this.filteredStatements, Exporter.this.qualifier, Exporter.this.qualifierDocument); + String[] var1Values = extractLabels(Exporter.this.filteredStatements, Exporter.this.variable1, Exporter.this.variable1Document); + String[] var2Values = extractLabels(Exporter.this.filteredStatements, Exporter.this.variable2, Exporter.this.variable2Document); + String[] qualValues = extractLabels(Exporter.this.filteredStatements, Exporter.this.qualifier, Exporter.this.qualifierDocument); if (dataTypes.get(Exporter.this.qualifier).equals("integer")) { int[] qual = Exporter.this.originalStatements.stream().mapToInt(s -> (int) s.get(Exporter.this.qualifier)).distinct().sorted().toArray(); if (qual.length < qualValues.length) { qualValues = IntStream.rangeClosed(qual[0], qual[qual.length - 1]) - .mapToObj(i -> String.valueOf(i)) + .mapToObj(String::valueOf) .toArray(String[]::new); } } @@ -1735,100 +1746,96 @@ public void computeKernelSmoothedTimeSlices() { } // create an array list of empty Matrix results, store all date-time stamps in them, and save indices in a hash map - Exporter.this.matrixResults = new ArrayList(); - HashMap timeMap = new HashMap<>(); + Exporter.this.matrixResults = new ArrayList<>(); if (Exporter.this.kernel.equals("gaussian")) { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by the start and end of the whole time range; the actual matrix is injected later if (timeWindow.equals("minutes")) { gamma = gamma.minusMinutes(1); - while (gamma.isBefore(e.plusMinutes(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusMinutes(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("hours")) { gamma = gamma.minusHours(1); - while (gamma.isBefore(e.plusHours(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusHours(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("days")) { gamma = gamma.minusDays(1); - while (gamma.isBefore(e.plusDays(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusDays(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("weeks")) { gamma = gamma.minusWeeks(1); - while (gamma.isBefore(e.plusWeeks(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusWeeks(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("months")) { gamma = gamma.minusMonths(1); - while (gamma.isBefore(e.plusMonths(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusMonths(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("years")) { gamma = gamma.minusYears(1); - while (gamma.isBefore(e.plusYears(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusYears(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } } else { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by width w; the actual matrix is injected later if (timeWindow.equals("minutes")) { gamma = gamma.minusMinutes(1); - while (gamma.isBefore(e.plusMinutes(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusMinutes(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMinutes(W_HALF).isBefore(b) ? b : gamma.minusMinutes(W_HALF), gamma, gamma.plusMinutes(W_HALF).isAfter(e) ? e : gamma.plusMinutes(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("hours")) { gamma = gamma.minusHours(1); - while (gamma.isBefore(e.plusHours(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusHours(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusHours(W_HALF).isBefore(b) ? b : gamma.minusHours(W_HALF), gamma, gamma.plusHours(W_HALF).isAfter(e) ? e : gamma.plusHours(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("days")) { gamma = gamma.minusDays(1); - while (gamma.isBefore(e.plusDays(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusDays(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusDays(W_HALF).isBefore(b) ? b : gamma.minusDays(W_HALF), gamma, gamma.plusDays(W_HALF).isAfter(e) ? e : gamma.plusDays(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("weeks")) { gamma = gamma.minusWeeks(1); - while (gamma.isBefore(e.plusWeeks(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusWeeks(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusWeeks(W_HALF).isBefore(b) ? b : gamma.minusWeeks(W_HALF), gamma, gamma.plusWeeks(W_HALF).isAfter(e) ? e : gamma.plusWeeks(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("months")) { gamma = gamma.minusMonths(1); - while (gamma.isBefore(e.plusMonths(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusMonths(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMonths(W_HALF).isBefore(b) ? b : gamma.minusMonths(W_HALF), gamma, gamma.plusMonths(W_HALF).isAfter(e) ? e : gamma.plusMonths(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } else if (timeWindow.equals("years")) { gamma = gamma.minusYears(1); - while (gamma.isBefore(e.plusYears(1))) { + while (gamma.isBefore(e)) { gamma = gamma.plusYears(1); Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusYears(W_HALF).isBefore(b) ? b : gamma.minusYears(W_HALF), gamma, gamma.plusYears(W_HALF).isAfter(e) ? e : gamma.plusYears(W_HALF))); - timeMap.put(gamma, Exporter.this.matrixResults.size() - 1); } } } - // create a 4D array, go through the statements, and populate the array - int[][][][] X = new int[var1Values.length][var2Values.length][qualValues.length][Exporter.this.matrixResults.size()]; // var1 x var2 x qual x time + // create a 3D array, go through the statements, and populate the array + @SuppressWarnings("unchecked") + ArrayList[][][] X = (ArrayList[][][]) new ArrayList[var1Values.length][var2Values.length][qualValues.length]; + for (int i = 0; i < var1Values.length; i++) { + for (int j = 0; j < var2Values.length; j++) { + for (int k = 0; k < qualValues.length; k++) { + X[i][j][k] = new ArrayList(); + } + } + } + Exporter.this.filteredStatements.stream().forEach(s -> { int var1Index = -1; if (Exporter.this.variable1Document) { @@ -1846,7 +1853,7 @@ public void computeKernelSmoothedTimeSlices() { var1Index = var1Map.get(s.getTitle()); } } else { - var1Index = var1Map.get((String) s.get(Exporter.this.variable1)); + var1Index = var1Map.get(((Entity) s.get(Exporter.this.variable1)).getValue()); } int var2Index = -1; if (Exporter.this.variable2Document) { @@ -1864,7 +1871,7 @@ public void computeKernelSmoothedTimeSlices() { var2Index = var2Map.get(s.getTitle()); } } else { - var2Index = var2Map.get((String) s.get(Exporter.this.variable2)); + var2Index = var2Map.get(((Entity) s.get(Exporter.this.variable2)).getValue()); } int qualIndex = -1; if (Exporter.this.qualifierDocument) { @@ -1885,46 +1892,53 @@ public void computeKernelSmoothedTimeSlices() { if (dataTypes.get(Exporter.this.qualifier).equals("integer") || dataTypes.get(Exporter.this.qualifier).equals("boolean")) { qualIndex = qualMap.get(String.valueOf((int) s.get(Exporter.this.qualifier))); } else { - qualIndex = qualMap.get(s.get(Exporter.this.qualifier)); + qualIndex = qualMap.get(((Entity) s.get(Exporter.this.qualifier)).getValue()); } } - int timeIndex = timeMap.get(s.getDateTime()); - X[var1Index][var2Index][qualIndex][timeIndex]++; + X[var1Index][var2Index][qualIndex].add(s); }); // process each matrix result in a parallel stream instead of for-loop and add calculation results - ArrayList processedResults = Exporter.this.matrixResults.parallelStream() - .map(matrixResult -> processTimeSlice(matrixResult, X, Exporter.this.matrixResults)) + ArrayList processedResults = ProgressBar.wrap(Exporter.this.matrixResults.parallelStream(), "Kernel smoothing") + .map(matrixResult -> processTimeSlice(matrixResult, X)) .collect(Collectors.toCollection(ArrayList::new)); Exporter.this.matrixResults = processedResults; } /** - * Compute a one-mode or two-mode network matrix with kernel-weighting and inject it into a {@link Matrix} object. - * - * To compute the kernel-weighted network projection, the 4D array X is needed because it stores the statement data, - * the current matrix result is needed because it stores the mid-point gamma, and the array list of all matrix - * results is needed because the mid-points of the matrices contain the time stamps for the time dimension in X, - * which is required to establish the time difference between the current matrix mid-point and the respective - * statement in X. + * Compute a one-mode or two-mode network matrix with kernel-weighting and inject it into a {@link Matrix} object. + * To compute the kernel-weighted network projection, the 3D array X with statement array lists corresponding to + * each i-j-k combination is needed because it stores the statement data including their date-time stamp, and + * the current matrix result is needed because it stores the mid-point gamma. The kernel-weighted temporal distance + * between the statement time and gamma is computed and used in creating the network. * * @param matrixResult The matrix result into which the network matrix will be inserted. - * @param X A 4D array containing the data. - * @param matrixResults All matrix results, containing the temporal information for the fourth dimension of X. - * @return The matrix result after inserting the network matrix. + * @param X A 3D array containing the data. + * @return The matrix result after inserting the network matrix. */ - private Matrix processTimeSlice(Matrix matrixResult, int[][][][] X, ArrayList matrixResults) { + private Matrix processTimeSlice(Matrix matrixResult, ArrayList[][][] X) { if (this.networkType.equals("twomode")) { double[][] m = new double[X.length][X[0].length]; for (int i = 0; i < X.length; i++) { for (int j = 0; j < X[0].length; j++) { for (int k = 0; k < X[0][0].length; k++) { - for (int t = 0; t < X[0][0][0].length; t++) { - m[i][j] = k * X[i][j][k][t] * zeta(matrixResults.get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + for (int t = 0; t < X[i][j][k].size(); t++) { + if (Exporter.this.qualifierAggregation.equals("ignore")) { + m[i][j] = m[i][j] + zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.qualifierAggregation.equals("subtract")) { + if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("boolean")) { + m[i][j] = m[i][j] + (((double) k) - 0.5) * 2 * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("integer")) { + m[i][j] = m[i][j] + k * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("short text")) { + m[i][j] = m[i][j] + zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } + } } } } } + matrixResult.setMatrix(m); } else if (this.networkType.equals("onemode")) { double[][] m = new double[X.length][X.length]; double[][] norm = new double[X.length][X.length]; @@ -1932,27 +1946,28 @@ private Matrix processTimeSlice(Matrix matrixResult, int[][][][] X, ArrayList { + eigenvalues[i] = computeNormalizedEigenvalues(Exporter.this.matrixResults.get(i).getMatrix(), "ojalgo"); // TODO: try out "apache", debug, and compare speed + }); + } + + ProgressBar.wrap(IntStream.range(0, Exporter.this.matrixResults.size()).parallel(), "Distance matrix").forEach(i -> { + IntStream.range(i + 1, Exporter.this.matrixResults.size()).forEach(j -> { // start from i + 1 to ensure symmetry and avoid redundant computation (= upper triangle) + double distance = 0.0; + if (distanceMethod.equals("spectral")) { + distance = spectralLoss(eigenvalues[i], eigenvalues[j]); + } else if (distanceMethod.equals("absdiff")) { // sum of element-wise absolute differences + for (int a = 0; a < Exporter.this.matrixResults.get(i).getMatrix().length; a++) { + for (int b = 0; b < Exporter.this.matrixResults.get(j).getMatrix()[0].length; b++) { + distance += Math.abs(Exporter.this.matrixResults.get(i).getMatrix()[a][b] - Exporter.this.matrixResults.get(j).getMatrix()[a][b]); + } + } + } + distanceMatrix[i][j] = distance; + distanceMatrix[j][i] = distance; // since the distance matrix is symmetric, set both [i][j] and [j][i] + }); + }); + return distanceMatrix; + } + /** * Create a series of one-mode or two-mode networks using a moving time window. */ @@ -2051,7 +2131,7 @@ public void computeTimeWindowMatrices() { ArrayList beforeStatements = new ArrayList(); // holds all statements between (and excluding) the time stamp of the first statement in the window and the focal statement ArrayList afterStatements = new ArrayList(); // holds all statements between (and excluding) the focal statement and the time stamp of the last statement in the window if (this.timeWindow.equals("events")) { - try (ProgressBar pb = new ProgressBar("Time window matrices...", this.filteredStatements.size())) { + try (ProgressBar pb = new ProgressBar("Time window matrices", this.filteredStatements.size())) { pb.stepTo(0); if (this.windowSize < 2) { LogEvent l = new LogEvent(Logger.WARNING, @@ -2155,7 +2235,7 @@ public void computeTimeWindowMatrices() { } } } else { - try (ProgressBar pb = new ProgressBar("Time window matrices...", 100)) { + try (ProgressBar pb = new ProgressBar("Time window matrices", 100)) { long percent = 0; pb.stepTo(percent); LocalDateTime startCalendar = this.startDateTime; // start of statement list @@ -2281,7 +2361,7 @@ public void exportToFile() { * can be used for estimating relational event models. */ private void eventCSV() { - try (ProgressBar pb = new ProgressBar("Exporting events...", this.filteredStatements.size())) { + try (ProgressBar pb = new ProgressBar("Exporting events", this.filteredStatements.size())) { pb.stepTo(0); String key; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -2340,7 +2420,7 @@ private void eventCSV() { * Export {@link Matrix Matrix} to a CSV matrix file. */ private void exportCSV() { - try (ProgressBar pb = new ProgressBar("Exporting networks...", this.matrixResults.size())) { + try (ProgressBar pb = new ProgressBar("Exporting networks", this.matrixResults.size())) { pb.stepTo(0); String filename2; String filename1 = this.outfile.substring(0, this.outfile.length() - 4); @@ -2397,7 +2477,7 @@ private void exportCSV() { * Export network to a DL fullmatrix file for the software UCINET. */ private void exportDL() { - try (ProgressBar pb = new ProgressBar("Exporting networks...", this.matrixResults.size())) { + try (ProgressBar pb = new ProgressBar("Exporting networks", this.matrixResults.size())) { pb.stepTo(0); String filename2; String filename1 = this.outfile.substring(0, this.outfile.length() - 4); @@ -2476,7 +2556,7 @@ private void exportDL() { * Export filter for graphML files. */ private void exportGraphml() { - try (ProgressBar pb = new ProgressBar("Exporting networks...", this.matrixResults.size())) { + try (ProgressBar pb = new ProgressBar("Exporting networks", this.matrixResults.size())) { pb.stepTo(0); // set up file name components for time window (in case this will be required later) @@ -3042,7 +3122,7 @@ public void saveSimulatedAnnealingBackboneResult(boolean penalty) { finalBackboneList.toArray(String[]::new), finalRedundantList.toArray(String[]::new), spectralLoss(eigenvaluesFull, eigenvaluesCurrent), - spectralLoss(eigenvaluesFull, computeNormalizedEigenvalues(redundantMatrix.getMatrix())), + spectralLoss(eigenvaluesFull, computeNormalizedEigenvalues(redundantMatrix.getMatrix(), "ojalgo")), p, T, temperatureLog.stream().mapToDouble(v -> v.doubleValue()).toArray(), @@ -3076,9 +3156,10 @@ public SimulatedAnnealingBackboneResult getSimulatedAnnealingBackboneResult() { * Use tools from the {@code ojalgo} library to compute eigenvalues of a symmetric matrix. * * @param matrix The matrix as a two-dimensional double array. + * @param library The linear algebra Java library to use as a back-end: {@code "ojalgo"} or {@code "apache"}. * @return One-dimensional double array of eigenvalues. */ - private double[] computeNormalizedEigenvalues(double[][] matrix) { + private double[] computeNormalizedEigenvalues(double[][] matrix, String library) { for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[0].length; j++) { if (matrix[i][j] < 0) { @@ -3086,21 +3167,36 @@ private double[] computeNormalizedEigenvalues(double[][] matrix) { } } } - Primitive64Matrix matrixPrimitive = Primitive64Matrix.FACTORY.rows(matrix); // create matrix - DenseArray rowSums = Primitive64Array.FACTORY.make(matrix.length); // container for row sums - matrixPrimitive.reduceRows(Aggregator.SUM, rowSums); // populate row sums into rowSums - Primitive64Matrix.SparseReceiver sr = Primitive64Matrix.FACTORY.makeSparse(matrix.length, matrix.length); // container for degree matrix - sr.fillDiagonal(rowSums); // put row sums onto diagonal - Primitive64Matrix laplacian = sr.get(); // put row sum container into a new degree matrix (the future Laplacian matrix) - laplacian.subtract(matrixPrimitive); // subtract adjacency matrix from degree matrix to create Laplacian matrix - Eigenvalue eig = Eigenvalue.PRIMITIVE.make(laplacian); // eigenvalues - eig.decompose(laplacian); // decomposition - double[] eigenvalues = eig.getEigenvalues().toRawCopy1D(); // extract eigenvalues and convert to double[] - double eigenvaluesSum = Arrays.stream(eigenvalues).sum(); // compute sum of eigenvalues - if (eigenvaluesSum > 0.0) { - eigenvalues = DoubleStream.of(eigenvalues).map(v -> v / eigenvaluesSum).toArray(); // normalise/scale to one + double[] eigenvalues; + if (library.equals("apache")) { + RealMatrix realMatrix = new Array2DRowRealMatrix(matrix); // create a real matrix from the 2D array + EigenDecomposition decomposition = new EigenDecomposition(realMatrix); // perform eigen decomposition + eigenvalues = decomposition.getRealEigenvalues(); // get the real parts of the eigenvalues + // normalize the eigenvalues + double eigenvaluesSum = Arrays.stream(eigenvalues).sum(); + if (eigenvaluesSum > 0.0) { + for (int i = 0; i < eigenvalues.length; i++) { + eigenvalues[i] /= eigenvaluesSum; + } + } + } else if (library.equals("ojalgo")) { + Primitive64Matrix matrixPrimitive = Primitive64Matrix.FACTORY.rows(matrix); // create matrix + DenseArray rowSums = Primitive64Array.FACTORY.make(matrix.length); // container for row sums + matrixPrimitive.reduceRows(Aggregator.SUM, rowSums); // populate row sums into rowSums + Primitive64Matrix.SparseReceiver sr = Primitive64Matrix.FACTORY.makeSparse(matrix.length, matrix.length); // container for degree matrix + sr.fillDiagonal(rowSums); // put row sums onto diagonal + Primitive64Matrix laplacian = sr.get(); // put row sum container into a new degree matrix (the future Laplacian matrix) + laplacian.subtract(matrixPrimitive); // subtract adjacency matrix from degree matrix to create Laplacian matrix + Eigenvalue eig = Eigenvalue.PRIMITIVE.make(laplacian); // eigenvalues + eig.decompose(laplacian); // decomposition + eigenvalues = eig.getEigenvalues().toRawCopy1D(); // extract eigenvalues and convert to double[] + double eigenvaluesSum = Arrays.stream(eigenvalues).sum(); // compute sum of eigenvalues + if (eigenvaluesSum > 0.0) { + eigenvalues = DoubleStream.of(eigenvalues).map(v -> v / eigenvaluesSum).toArray(); // normalize/scale to one + } + } else { + eigenvalues = new double[matrix.length]; // return zeroes if library not recognized; don't log error because it would be very slow } - return eigenvalues; } @@ -3219,7 +3315,7 @@ public void initializeNestedBackbone() { this.isolates = true; // include isolates in the iterations but not in the full matrix; will be adjusted to smaller full matrix dimensions without isolates manually each time in the iterations; necessary because some actors may be deleted in the backbone matrix otherwise after deleting their concepts // compute normalised eigenvalues for the full matrix; no need to recompute every time as they do not change - eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix()); + eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix(), "ojalgo"); iteration = new int[fullConcepts.length]; backboneLoss = new double[fullConcepts.length]; redundantLoss = new double[fullConcepts.length]; @@ -3255,7 +3351,7 @@ public void iterateNestedBackbone() { candidateMatrix = this.computeOneModeMatrix(candidateStatementList, this.qualifierAggregation, this.startDateTime, this.stopDateTime); candidateMatrix = this.reduceCandidateMatrix(candidateMatrix, fullMatrix.getRowNames()); // ensure it has the right dimensions by purging isolates relative to the full matrix candidateMatrices.add(candidateMatrix); - eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix()); // normalised eigenvalues for the candidate matrix + eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix(), "ojalgo"); // normalised eigenvalues for the candidate matrix currentLosses[i] = spectralLoss(eigenvaluesFull, eigenvaluesCandidate); } double smallestLoss = 0.0; @@ -3280,7 +3376,7 @@ public void iterateNestedBackbone() { Matrix redundantMatrix = this.computeOneModeMatrix(candidateStatementList, this.qualifierAggregation, this.startDateTime, this.stopDateTime); redundantMatrix = this.reduceCandidateMatrix(redundantMatrix, fullMatrix.getRowNames()); redundantMatrices.add(redundantMatrix); - eigenvaluesCandidate = computeNormalizedEigenvalues(redundantMatrix.getMatrix()); + eigenvaluesCandidate = computeNormalizedEigenvalues(redundantMatrix.getMatrix(), "ojalgo"); redundantLoss[counter] = spectralLoss(eigenvaluesFull, eigenvaluesCandidate); numStatements[counter] = numStatementsCandidates[i]; counter++; @@ -3339,7 +3435,7 @@ public void initializeSimulatedAnnealingBackbone(boolean penalty, double p, int this.isolates = true; // include isolates in the iterations; will be adjusted to full matrix without isolates manually each time // compute normalised eigenvalues for the full matrix; no need to recompute every time as they do not change - eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix()); + eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix(), "ojalgo"); if (penalty) { // simulated annealing with penalty: initially one randomly chosen entity in the backbone set // pick a random concept c_j from C and save its index @@ -3391,7 +3487,7 @@ public void initializeSimulatedAnnealingBackbone(boolean penalty, double p, int finalMatrix = this.reduceCandidateMatrix(finalMatrix, fullMatrix.getRowNames()); // ensure it has the right dimensions by purging isolates relative to the full matrix // eigenvalues for final matrix - eigenvaluesFinal = computeNormalizedEigenvalues(finalMatrix.getMatrix()); // normalised eigenvalues for the candidate matrix + eigenvaluesFinal = computeNormalizedEigenvalues(finalMatrix.getMatrix(), "ojalgo"); // normalised eigenvalues for the candidate matrix // create an initial current backbone set B_0, also with the one c_j concept like in B: B_0 <- {c_j} currentBackboneList = new ArrayList(finalBackboneList); @@ -3504,7 +3600,7 @@ public void iterateSimulatedAnnealingBackbone(boolean penalty) { .collect(Collectors.toCollection(ArrayList::new)); candidateMatrix = this.computeOneModeMatrix(candidateStatementList, this.qualifierAggregation, this.startDateTime, this.stopDateTime); // create candidate matrix after filtering the statements based on the action that was executed candidateMatrix = this.reduceCandidateMatrix(candidateMatrix, fullMatrix.getRowNames()); // ensure it has the right dimensions by purging isolates relative to the full matrix - eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix()); // normalised eigenvalues for the candidate matrix + eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix(), "ojalgo"); // normalised eigenvalues for the candidate matrix if (penalty) { newLoss = penalizedLoss(eigenvaluesFull, eigenvaluesCandidate, p, candidateBackboneList.size(), fullConcepts.length); // spectral distance between full and candidate matrix } else { @@ -3583,7 +3679,7 @@ public double[] evaluateBackboneSolution(String[] backboneEntities, int p) { this.isolates = true; // include isolates in the iterations; will be adjusted to full matrix without isolates manually each time // compute normalised eigenvalues for the full matrix; no need to recompute every time as they do not change - eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix()); + eigenvaluesFull = computeNormalizedEigenvalues(fullMatrix.getMatrix(), "ojalgo"); // create copy of filtered statements and remove redundant entities ArrayList entityList = Stream.of(backboneEntities).collect(Collectors.toCollection(ArrayList::new)); @@ -3604,7 +3700,7 @@ public double[] evaluateBackboneSolution(String[] backboneEntities, int p) { .collect(Collectors.toCollection(ArrayList::new)); candidateMatrix = this.computeOneModeMatrix(candidateStatementList, this.qualifierAggregation, this.startDateTime, this.stopDateTime); // create candidate matrix after filtering the statements based on the action that was executed candidateMatrix = this.reduceCandidateMatrix(candidateMatrix, fullMatrix.getRowNames()); // ensure it has the right dimensions by purging isolates relative to the full matrix - eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix()); // normalised eigenvalues for the candidate matrix + eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix(), "ojalgo"); // normalised eigenvalues for the candidate matrix results[0] = penalizedLoss(eigenvaluesFull, eigenvaluesCandidate, p, backboneSet.size(), fullConcepts.length); // spectral distance between full and candidate matrix // spectral distance between full and redundant set @@ -3614,7 +3710,7 @@ public double[] evaluateBackboneSolution(String[] backboneEntities, int p) { .collect(Collectors.toCollection(ArrayList::new)); candidateMatrix = this.computeOneModeMatrix(candidateStatementList, this.qualifierAggregation, this.startDateTime, this.stopDateTime); // create candidate matrix after filtering the statements based on the action that was executed candidateMatrix = this.reduceCandidateMatrix(candidateMatrix, fullMatrix.getRowNames()); // ensure it has the right dimensions by purging isolates relative to the full matrix - eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix()); // normalised eigenvalues for the candidate matrix + eigenvaluesCandidate = computeNormalizedEigenvalues(candidateMatrix.getMatrix(), "ojalgo"); // normalised eigenvalues for the candidate matrix results[1] = penalizedLoss(eigenvaluesFull, eigenvaluesCandidate, p, redundantSet.size(), fullConcepts.length); // spectral distance between full and candidate matrix return results; diff --git a/rDNA/build.gradle b/rDNA/build.gradle index 3c29bc93..194ccbdf 100644 --- a/rDNA/build.gradle +++ b/rDNA/build.gradle @@ -1,4 +1,5 @@ task rDNADocument { + inputs.dir 'rDNA' doLast { exec { workingDir 'rDNA' @@ -9,26 +10,25 @@ task rDNADocument { task rDNABuild { dependsOn rDNADocument + inputs.dir 'rDNA' + def outputDir = file("$rootDir/build/") + doFirst { + delete fileTree(dir: outputDir, includes: ['*.tar.gz']) + } doLast { - exec { - workingDir '.' - commandLine 'mkdir', "-p", "$rootDir/build" - } + mkdir "$rootDir/build" exec { workingDir "$rootDir/build/" commandLine 'R', "CMD", "build", "../rDNA/rDNA" } } - def outputDir = file("$rootDir/build/") outputs.dir outputDir } task rDNACheck { + inputs.dir 'rDNA' doLast { - exec { - workingDir '.' - commandLine 'mkdir', "-p", "$rootDir/build" - } + mkdir "$rootDir/build" exec { workingDir "rDNA" commandLine 'R', "-e", "devtools::check(manual = TRUE, check_dir = '../../build/')" @@ -39,11 +39,9 @@ task rDNACheck { } task rDNATest { + inputs.dir 'rDNA' doLast { - exec { - workingDir '.' - commandLine 'mkdir', "-p", "$rootDir/build" - } + mkdir "$rootDir/build" exec { workingDir "rDNA" commandLine 'R', "-e", "devtools::test()" @@ -57,6 +55,7 @@ task rDNATest { task build { dependsOn rDNABuild + inputs.dir 'rDNA' def outputDir = file("$rootDir/build/") outputs.dir outputDir doLast {} diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index b5a8fe9d..504ace0b 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2023-10-15 +Date: 2024-02-04 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", diff --git a/rDNA/rDNA/NAMESPACE b/rDNA/rDNA/NAMESPACE index 750c465f..a449fcf4 100644 --- a/rDNA/rDNA/NAMESPACE +++ b/rDNA/rDNA/NAMESPACE @@ -20,6 +20,7 @@ export(dna_barplot) export(dna_closeDatabase) export(dna_evaluateBackboneSolution) export(dna_getAttributes) +export(dna_getHeadlessDna) export(dna_getVariables) export(dna_init) export(dna_jar) @@ -28,6 +29,7 @@ export(dna_network) export(dna_openConnectionProfile) export(dna_openDatabase) export(dna_phaseTransitions) +export(dna_phaseTransitions2) export(dna_printDetails) export(dna_queryCoders) export(dna_sample) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 167626f7..8703bb2e 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -73,6 +73,34 @@ dna_init <- function(jarfile = dna_jar(), memory = 1024, returnString = FALSE) { } } +#' Get headless DNA +#' +#' Get headless DNA. +#' +#' This function returns a reference to the Java class that represents the +#' R interface of DNA, i.e., the headless API. Headless means that no graphical +#' user interface is necessary to use the functions in DNA. This is the raw API +#' interface and should only be used by expert users or to debug other rDNA +#' functions. You can save the headless DNA object to an object in R and access +#' its Java functions directly as slots using the "$" operator, for example +#' \code{dna_getHeadlessDna()$getExport()} will return a reference to the +#' \code{Exporter} class, which does all the network computations, and +#' \code{dna_getHeadlessDna()$getAttributes()} will return attributes etc. You +#' can view the different functions that are available by using the tab key to +#' auto-complete function access after typing the "$" if the headless reference +#' has been saved to an object in the workspace. +#' +#' @return Headless DNA reference. +#' +#' @author Philip Leifeld +#' +#' @family {rDNA package startup} +#' +#' @export +dna_getHeadlessDna <- function() { + dnaEnvironment[["dna"]]$headlessDna +} + #' Identify and/or download and install the correct DNA jar file #' #' Identify and/or download and install the correct DNA jar file. @@ -987,6 +1015,18 @@ dna_getAttributes <- function(statementType = NULL, #' @param windowSize The number of time units of which a moving time window is #' comprised. This can be the number of statement events, the number of days #' etc., as defined in the \code{"timeWindow"} argument. +#' @param kernel Use kernel smoothing for computing time windows? This option +#' only matters if the \code{timeWindow} argument has a value other than +#' \code{"no"} or \code{"event"}. The default value \code{kernel = "no"} +#' switches off kernel smoothing, which means all statements within a time +#' window are weighted equally. Other values down-weight statements the +#' farther they are temporally away from the mid-point of the time window. +#' Several kernel smoothing functions are available, similar to kernel density +#' estimation: \code{"uniform"} is similar to \code{"no"} and weights all +#' statements with a value of \code{0.5}. \code{"gaussian"} uses a standard +#' normal distribution as a kernel smoother. \code{"epanechnikov"} uses an +#' Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel +#' function. If in doubt, do not use kernel smoothing. #' @param excludeValues A list of named character vectors that contains entries #' which should be excluded during network construction. For example, #' \code{list(concept = c("A", "B"), organization = c("org A", "org B"))} @@ -1083,6 +1123,7 @@ dna_network <- function(networkType = "twomode", stop.time = "23:59:59", timeWindow = "no", windowSize = 100, + kernel = "no", excludeValues = list(), excludeAuthors = character(), excludeSources = character(), @@ -1136,7 +1177,7 @@ dna_network <- function(networkType = "twomode", } # call rNetwork function to compute results - .jcall(dnaEnvironment[["dna"]]$headlessDna, + .jcall(dna_getHeadlessDna(), "V", "rNetwork", networkType, @@ -1157,6 +1198,7 @@ dna_network <- function(networkType = "twomode", stop.time, timeWindow, as.integer(windowSize), + kernel, var, val, excludeAuthors, @@ -1172,7 +1214,7 @@ dna_network <- function(networkType = "twomode", fileFormat ) - exporter <- .jcall(dnaEnvironment[["dna"]]$headlessDna, "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored + exporter <- .jcall(dna_getHeadlessDna(), "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored if (networkType == "eventlist") { # assemble an event list in the form of a data frame of filtered statements f <- J(exporter, "getFilteredStatements", simplify = TRUE) # array list of filtered export statements; use J because array list return type not recognized using .jcall @@ -4300,6 +4342,14 @@ print.dna_multiclust <- function(x, ...) { #' be applied to identify the different stages and phases from the resulting #' distance matrix. #' +#' In contrast to the \code{\link{dna_phaseTransitions}} function, the +#' \code{\link{dna_phaseTransitions2}} function does not offer split nodes. +#' However, it offers two advantages. The first one is faster computation and +#' better parallelization in Java. The second one is kernel smoothing, which +#' means the farther away from a time point a statement is, the less important +#' it becomes for the network that is created around the time point. Several +#' kernel smoothing functions are available; see the \code{kernel} argument. +#' #' @param distanceMethod The distance measure that expresses the dissimilarity #' between any two network matrices. The following choices are available: #' \itemize{ @@ -4388,7 +4438,21 @@ print.dna_multiclust <- function(x, ...) { #' the \pkg{pbmcapply} package is used to parallelize the computation of the #' distance matrix and the clustering. Note that this method is based on #' forking and is only available on Unix operating systems, including MacOS -#' and Linux. +#' and Linux. In the \code{dna_phaseTransitions2} function, only the +#' clustering is done using this parallelization in R while the remaining +#' computations are done in parallel using threads in Java, which is more +#' efficient. +#' @param kernel Use kernel smoothing for computing network time slices? The +#' default value \code{kernel = "no"} switches off kernel smoothing, which +#' means all statements within a time window are weighted equally. Other +#' values down-weight statements the farther they are temporally away from the +#' temporal mid-point of the respective time slice. Several kernel smoothing +#' functions are available, similar to kernel density estimation: +#' \code{"uniform"} is similar to \code{"no"} and weights all statements with +#' a value of \code{0.5}. \code{"gaussian"} uses a standard normal +#' distribution as a kernel smoother. \code{"epanechnikov"} uses an +#' Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel +#' function. #' @inheritParams dna_network #' #' @examples @@ -4412,6 +4476,25 @@ print.dna_multiclust <- function(x, ...) { #' timeWindow = "events", #' windowSize = 15) #' results +#' +#' # kernel smoothing and spectral distances using dna_phaseTransitions2 +#' results2 <- dna_phaseTransitions2(distanceMethod = "spectral", +#' clusterMethods = c("ward", +#' "pam", +#' "concor", +#' "walktrap"), +#' k.min = 2, +#' k.max = 6, +#' networkType = "onemode", +#' variable1 = "organization", +#' variable2 = "concept", +#' timeWindow = "days", +#' windowSize = 15, +#' kernel = "gaussian") +#' results2 +#' +#' library("ggplot2") +#' autoplot(results2) #' } #' #' @author Philip Leifeld, Kristijan Garic @@ -4519,6 +4602,7 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", stop.time = stop.time, timeWindow = timeWindow, windowSize = windowSize, + kernel = "no", excludeValues = excludeValues, excludeAuthors = excludeAuthors, excludeSources = excludeSources, @@ -4599,6 +4683,7 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", stop.time = stop.time, timeWindow = timeWindow, windowSize = windowSize, + kernel = "no", excludeValues = excludeValues, excludeAuthors = excludeAuthors, excludeSources = excludeSources, @@ -4823,6 +4908,340 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", return(results) } +#' @rdname dna_phaseTransitions +#' @author Philip Leifeld +#' @importFrom stats dist +#' @importFrom utils combn +#' @importFrom rJava .jarray .jcall .jnull J +#' @export +dna_phaseTransitions2 <- function(distanceMethod = "absdiff", + normalizeNetwork = FALSE, + clusterMethods = c("single", + "average", + "complete", + "ward", + "kmeans", + "pam", + "spectral", + "fastgreedy", + "walktrap"), + k.min = 2, + k.max = 6, + cores = 1, + networkType = "twomode", + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "subtract", + normalization = "no", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + timeWindow = "days", + windowSize = 150, + kernel = "no", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE) { + + # check arguments and packages + if (distanceMethod == "spectral" && networkType == "twomode") { + distanceMethod <- "absdiff" + warning("Spectral distances only work with one-mode networks. Using 'distanceMethod = \"absdiff\"' instead.") + } + if (cores > 1 && !requireNamespace("pbmcapply", quietly = TRUE)) { + pbmclapply <- FALSE + warning("Argument 'cores' requires the 'pbmcapply' package, which is not installed.\nSetting 'cores = 1'. Consider installing the 'pbmcapply' package if you use Linux or MacOS.") + } + igraphMethods <- c("louvain", "fastgreedy", "walktrap", "leading_eigen", "edge_betweenness", "infomap", "label_prop", "spinglass") + if (any(igraphMethods %in% clusterMethods) && !requireNamespace("igraph", quietly = TRUE)) { + clusterMethods <- clusterMethods[-igraphMethods] + warning("'igraph' package not installed. Dropping clustering methods from the 'igraph' package. Consider installing 'igraph'.") + } + if ("pam" %in% clusterMethods && !requireNamespace("cluster", quietly = TRUE)) { + clusterMethods <- clusterMethods[which(clusterMethods != "pam")] + warning("'cluster' package not installed. Dropping clustering methods from the 'cluster' package. Consider installing 'cluster'.") + } + if ("concor" %in% clusterMethods && k.min > 2) { + clusterMethods <- clusterMethods[which(clusterMethods != "concor")] + warning("Dropping 'concor' from clustering methods because the CONCOR implementation in rDNA can only find exactly two clusters, but the 'k.min' argument was larger than 2.") + } + clusterMethods <- rev(clusterMethods) # reverse order to save time during parallel computation by starting the computationally intensive methods first + mcall <- match.call() # save the arguments for storing them in the results later + + # generate the time window networks + if (is.null(timeWindow) || is.na(timeWindow) || !is.character(timeWindow) || length(timeWindow) != 1 || !timeWindow %in% c("events", "seconds", "minutes", "hours", "days", "weeks", "months", "years")) { + timeWindow <- "events" + warning("The 'timeWindow' argument was invalid. Proceeding with 'timeWindow = \"events\" instead.") + } + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + fileFormat <- .jnull(class = "java/lang/String") + outfile <- .jnull(class = "java/lang/String") + + # call rNetwork function to compute results + .jcall(dna_getHeadlessDna(), + "V", + "rNetwork", + networkType, + statementType, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + TRUE, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + timeWindow, + as.integer(windowSize), + kernel, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes, + outfile, + fileFormat + ) + exporter <- dna_getHeadlessDna()$getExporter() # save Java object reference to exporter class + + # matrix normalization + if (normalizeNetwork) { + .jcall(exporter, + "V", + "normalizeMatrixResults") + } + + # compute distance matrix + if (distanceMethod == "modularity") { + stop("Differences in modularity have not been implemented yet. Please use absolute differences or spectral Euclidean distance as a distance method.") + } else if (!distanceMethod %in% c("absdiff", "spectral")) { + stop("Distance method not recognized. Try \"absdiff\" or \"spectral\".") + } + distance_mat <- .jcall(exporter, + "[[D", + "computeDistanceMatrix", + distanceMethod, + simplify = TRUE) + distance_mat <- distance_mat / max(distance_mat) # rescale between 0 and 1 + + # retrieve mid-point dates (gamma) + m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object + dates <- sapply(m, function(x) as.POSIXct(.jcall(x, "J", "getDateTimeLong"), origin = "1970-01-01")) + + # define clustering function + hclustMethods <- c("single", "average", "complete", "ward") + cl <- function(method, distmat) { + tryCatch({ + similarity_mat <- 1 - distmat + g <- igraph::graph.adjacency(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance + if (method %in% hclustMethods) { + if (method == "single") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "single")) + } else if (method == "average") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "average")) + } else if (method == "complete") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "complete")) + } else if (method == "ward") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "ward.D2")) + } + opt_k <- lapply(k.min:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "kmeans") { + opt_k <- lapply(k.min:k.max, function(x) { + suppressWarnings(cl <- stats::kmeans(distmat, centers = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "pam") { + opt_k <- lapply(k.min:k.max, function(x) { + suppressWarnings(cl <- cluster::pam(distmat, k = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "spectral") { + sigma <- 1.0 + affinity_matrix <- exp(-distmat^2 / (2 * sigma^2)) + L <- diag(rowSums(affinity_matrix)) - affinity_matrix + D.sqrt.inv <- diag(1 / sqrt(rowSums(affinity_matrix))) + L.norm <- D.sqrt.inv %*% L %*% D.sqrt.inv + eigenvalues <- eigen(L.norm) # eigenvalue decomposition + opt_k <- lapply(k.min:k.max, function(x) { + U <- eigenvalues$vectors[, 1:x] + mem <- kmeans(U, centers = x)$cluster # cluster the eigenvectors + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "concor") { + suppressWarnings(mi <- stats::cor(similarity_mat)) + iter <- 1 + while (any(abs(mi) <= 0.999) & iter <= 50) { + mi[is.na(mi)] <- 0 + mi <- stats::cor(mi) + iter <- iter + 1 + } + mem <- ((mi[, 1] > 0) * 1) + 1 + } else if (method %in% igraphMethods) { + if (method == "fastgreedy") { + suppressWarnings(cl <- igraph::cluster_fast_greedy(g)) + } else if (method == "walktrap") { + suppressWarnings(cl <- igraph::cluster_walktrap(g)) + } else if (method == "leading_eigen") { + suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) + } else if (method == "edge_betweenness") { + suppressWarnings(cl <- igraph::cluster_edge_betweenness(g)) + } else if (method == "spinglass") { + suppressWarnings(cl <- igraph::cluster_spinglass(g)) + } + opt_k <- lapply(k.min:k.max, function(x) { + mem <- igraph::cut_at(communities = cl, no = x) + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } + list(method = method, + modularity = igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem), + memberships = mem) + }, + error = function(e) { + warning("Cluster method '", method, "' could not be computed due to an error: ", e) + }, + warning = function(w) { + warning("Cluster method '", method, "' threw a warning: ", w) + }) + } + + # apply all clustering methods to distance matrix + if (cores > 1) { + cat(paste("Clustering distance matrix on", cores, "cores.\n")) + a <- Sys.time() + l <- pbmcapply::pbmclapply(clusterMethods, cl, distmat = distance_mat, mc.cores = cores) + b <- Sys.time() + } else { + cat("Clustering distance matrix... ") + a <- Sys.time() + l <- lapply(clusterMethods, cl, distmat = distance_mat) + b <- Sys.time() + cat(intToUtf8(0x2714), "\n") + } + print(b - a) + for (i in length(l):1) { + if (length(l[[i]]) == 1) { + l <- l[-i] + clusterMethods <- clusterMethods[-i] + } + } + results <- list() + mod <- sapply(l, function(x) x$modularity) + best <- which(mod == max(mod))[1] + results$modularity <- mod[best] + results$clusterMethod <- clusterMethods[best] + + # temporal embedding via MDS + if (!requireNamespace("MASS", quietly = TRUE)) { + mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + "state" = l[[best]]$memberships) + results$states <- mem + warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") + } else { + cat("Temporal embedding...\n") + a <- Sys.time() + distmat <- distance_mat + 1e-12 + mds <- MASS::isoMDS(distmat) # MDS of distance matrix + points <- mds$points + mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + "state" = l[[best]]$memberships, + "X1" = points[, 1], + "X2" = points[, 2]) + results$states <- mem + b <- Sys.time() + print(b - a) + } + + results$distmat <- distance_mat + class(results) <- "dna_phaseTransitions" + attributes(results)$stress <- ifelse(ncol(results$states) == 2, NA, mds$stress) + attributes(results)$call <- mcall + return(results) +} + #' Print the summary of a \code{dna_phaseTransitions} object #' #' Show details of a \code{dna_phaseTransitions} object. diff --git a/rDNA/rDNA/man/dna_getHeadlessDna.Rd b/rDNA/rDNA/man/dna_getHeadlessDna.Rd new file mode 100644 index 00000000..dce52f07 --- /dev/null +++ b/rDNA/rDNA/man/dna_getHeadlessDna.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rDNA.R +\name{dna_getHeadlessDna} +\alias{dna_getHeadlessDna} +\title{Get headless DNA} +\usage{ +dna_getHeadlessDna() +} +\value{ +Headless DNA reference. +} +\description{ +Get headless DNA. +} +\details{ +This function returns a reference to the Java class that represents the +R interface of DNA, i.e., the headless API. Headless means that no graphical +user interface is necessary to use the functions in DNA. This is the raw API +interface and should only be used by expert users or to debug other rDNA +functions. You can save the headless DNA object to an object in R and access +its Java functions directly as slots using the "$" operator, for example +\code{dna_getHeadlessDna()$getExport()} will return a reference to the +\code{Exporter} class, which does all the network computations, and +\code{dna_getHeadlessDna()$getAttributes()} will return attributes etc. You +can view the different functions that are available by using the tab key to +auto-complete function access after typing the "$" if the headless reference +has been saved to an object in the workspace. +} +\seealso{ +Other {rDNA package startup}: +\code{\link{dna_init}()}, +\code{\link{dna_jar}()}, +\code{\link{dna_sample}()} +} +\author{ +Philip Leifeld +} +\concept{{rDNA package startup}} diff --git a/rDNA/rDNA/man/dna_init.Rd b/rDNA/rDNA/man/dna_init.Rd index 04574283..c72d8c6f 100644 --- a/rDNA/rDNA/man/dna_init.Rd +++ b/rDNA/rDNA/man/dna_init.Rd @@ -36,6 +36,7 @@ dna_init() } \seealso{ Other {rDNA package startup}: +\code{\link{dna_getHeadlessDna}()}, \code{\link{dna_jar}()}, \code{\link{dna_sample}()} } diff --git a/rDNA/rDNA/man/dna_jar.Rd b/rDNA/rDNA/man/dna_jar.Rd index 127c046f..82e4af72 100644 --- a/rDNA/rDNA/man/dna_jar.Rd +++ b/rDNA/rDNA/man/dna_jar.Rd @@ -33,6 +33,7 @@ file. If all of this fails, an error message is thrown. } \seealso{ Other {rDNA package startup}: +\code{\link{dna_getHeadlessDna}()}, \code{\link{dna_init}()}, \code{\link{dna_sample}()} } diff --git a/rDNA/rDNA/man/dna_network.Rd b/rDNA/rDNA/man/dna_network.Rd index 4799220e..f6dbba3c 100644 --- a/rDNA/rDNA/man/dna_network.Rd +++ b/rDNA/rDNA/man/dna_network.Rd @@ -23,6 +23,7 @@ dna_network( stop.time = "23:59:59", timeWindow = "no", windowSize = 100, + kernel = "no", excludeValues = list(), excludeAuthors = character(), excludeSources = character(), @@ -181,6 +182,19 @@ event time.} comprised. This can be the number of statement events, the number of days etc., as defined in the \code{"timeWindow"} argument.} +\item{kernel}{Use kernel smoothing for computing time windows? This option +only matters if the \code{timeWindow} argument has a value other than +\code{"no"} or \code{"event"}. The default value \code{kernel = "no"} +switches off kernel smoothing, which means all statements within a time +window are weighted equally. Other values down-weight statements the +farther they are temporally away from the mid-point of the time window. +Several kernel smoothing functions are available, similar to kernel density +estimation: \code{"uniform"} is similar to \code{"no"} and weights all +statements with a value of \code{0.5}. \code{"gaussian"} uses a standard +normal distribution as a kernel smoother. \code{"epanechnikov"} uses an +Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel +function. If in doubt, do not use kernel smoothing.} + \item{excludeValues}{A list of named character vectors that contains entries which should be excluded during network construction. For example, \code{list(concept = c("A", "B"), organization = c("org A", "org B"))} diff --git a/rDNA/rDNA/man/dna_phaseTransitions.Rd b/rDNA/rDNA/man/dna_phaseTransitions.Rd index d1a8343f..2fcd8b96 100644 --- a/rDNA/rDNA/man/dna_phaseTransitions.Rd +++ b/rDNA/rDNA/man/dna_phaseTransitions.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/rDNA.R \name{dna_phaseTransitions} \alias{dna_phaseTransitions} +\alias{dna_phaseTransitions2} \alias{print.dna_phaseTransitions} \alias{autoplot.dna_phaseTransitions} \title{Detect phase transitions and states in a discourse network} @@ -44,6 +45,44 @@ dna_phaseTransitions( invertTypes = FALSE ) +dna_phaseTransitions2( + distanceMethod = "absdiff", + normalizeNetwork = FALSE, + clusterMethods = c("single", "average", "complete", "ward", "kmeans", "pam", + "spectral", "fastgreedy", "walktrap"), + k.min = 2, + k.max = 6, + cores = 1, + networkType = "twomode", + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "subtract", + normalization = "no", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + timeWindow = "days", + windowSize = 150, + kernel = "no", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE +) + \method{print}{dna_phaseTransitions}(x, ...) \method{autoplot}{dna_phaseTransitions}(object, ..., plots = c("heatmap", "silhouette", "mds", "states")) @@ -143,7 +182,10 @@ the data in this case. Do not use with actor networks!} the \pkg{pbmcapply} package is used to parallelize the computation of the distance matrix and the clustering. Note that this method is based on forking and is only available on Unix operating systems, including MacOS -and Linux.} +and Linux. In the \code{dna_phaseTransitions2} function, only the +clustering is done using this parallelization in R while the remaining +computations are done in parallel using threads in Java, which is more +efficient.} \item{networkType}{The kind of network to be computed. Can be \code{"twomode"}, \code{"onemode"}, or \code{"eventlist"}.} @@ -339,6 +381,18 @@ construction (\code{invertTypes = FALSE}) or if they should be the only values that should be included during network construction (\code{invertTypes = TRUE}).} +\item{kernel}{Use kernel smoothing for computing network time slices? The +default value \code{kernel = "no"} switches off kernel smoothing, which +means all statements within a time window are weighted equally. Other +values down-weight statements the farther they are temporally away from the +temporal mid-point of the respective time slice. Several kernel smoothing +functions are available, similar to kernel density estimation: +\code{"uniform"} is similar to \code{"no"} and weights all statements with +a value of \code{0.5}. \code{"gaussian"} uses a standard normal +distribution as a kernel smoother. \code{"epanechnikov"} uses an +Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel +function.} + \item{x}{A \code{dna_phaseTransitions} object.} \item{...}{Additional arguments. Currently not in use.} @@ -363,6 +417,14 @@ Euclidean spectral distance are available. Several clustering techniques can be applied to identify the different stages and phases from the resulting distance matrix. +In contrast to the \code{\link{dna_phaseTransitions}} function, the +\code{\link{dna_phaseTransitions2}} function does not offer split nodes. +However, it offers two advantages. The first one is faster computation and +better parallelization in Java. The second one is kernel smoothing, which +means the farther away from a time point a statement is, the less important +it becomes for the network that is created around the time point. Several +kernel smoothing functions are available; see the \code{kernel} argument. + Print a summary of a \code{dna_phaseTransitions} object, which can be created using the \link{dna_phaseTransitions} function. } @@ -387,6 +449,25 @@ results <- dna_phaseTransitions(distanceMethod = "absdiff", timeWindow = "events", windowSize = 15) results + +# kernel smoothing and spectral distances using dna_phaseTransitions2 +results2 <- dna_phaseTransitions2(distanceMethod = "spectral", + clusterMethods = c("ward", + "pam", + "concor", + "walktrap"), + k.min = 2, + k.max = 6, + networkType = "onemode", + variable1 = "organization", + variable2 = "concept", + timeWindow = "days", + windowSize = 15, + kernel = "gaussian") +results2 + +library("ggplot2") +autoplot(results2) } } diff --git a/rDNA/rDNA/man/dna_sample.Rd b/rDNA/rDNA/man/dna_sample.Rd index 1cb11311..20e3a0e3 100644 --- a/rDNA/rDNA/man/dna_sample.Rd +++ b/rDNA/rDNA/man/dna_sample.Rd @@ -27,6 +27,7 @@ dna_openDatabase(s) } \seealso{ Other {rDNA package startup}: +\code{\link{dna_getHeadlessDna}()}, \code{\link{dna_init}()}, \code{\link{dna_jar}()} } From 8118bf9ab764332bd234e60d844f119173e3f549 Mon Sep 17 00:00:00 2001 From: leifeld Date: Sun, 4 Feb 2024 12:25:17 +0000 Subject: [PATCH 08/43] Bugfix in Importer class with duplicate entities and attributes from DNA 2 --- dna/src/main/java/gui/Importer.java | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/dna/src/main/java/gui/Importer.java b/dna/src/main/java/gui/Importer.java index 4ef24333..f51539cf 100644 --- a/dna/src/main/java/gui/Importer.java +++ b/dna/src/main/java/gui/Importer.java @@ -918,6 +918,19 @@ public void run() { } documentSelectSql = documentSelectSql + ");"; + String d17sql = ""; + String d20sql = ""; + if (Dna.sql.getConnectionProfile().getType().equals("sqlite")) { + d17sql = "INSERT OR IGNORE INTO ATTRIBUTEVALUES (EntityId, AttributeVariableId, AttributeValue) VALUES (?, ?, ?);"; + d20sql = "INSERT OR IGNORE INTO ENTITIES (VariableId, Value, Red, Green, Blue) VALUES (?, ?, ?, ?, ?);"; + } else if (Dna.sql.getConnectionProfile().getType().equals("mysql")) { + d17sql = "INSERT IGNORE INTO ATTRIBUTEVALUES (EntityId, AttributeVariableId, AttributeValue) VALUES (?, ?, ?);"; + d20sql = "INSERT IGNORE INTO ENTITIES (VariableId, Value, Red, Green, Blue) VALUES (?, ?, ?, ?, ?);"; + } else if (Dna.sql.getConnectionProfile().getType().equals("postgresql")) { + d17sql = "INSERT INTO ATTRIBUTEVALUES (EntityId, AttributeVariableId, AttributeValue) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE EntityId = VALUES(EntityId), AttributeVariableId = VALUES(AttributeVariableId);"; + d20sql = "INSERT INTO ENTITIES (VariableId, Value, Red, Green, Blue) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE VariableId = VALUES(VariableId), Value = VALUES(Value);"; + } + try (Connection connForeign = Importer.this.sql.getDataSource().getConnection(); Connection connDomestic = Dna.sql.getDataSource().getConnection(); PreparedStatement d1 = connDomestic.prepareStatement("INSERT INTO DOCUMENTS (Title, Text, Coder, Author, Source, Section, Notes, Type, Date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", PreparedStatement.RETURN_GENERATED_KEYS); @@ -944,10 +957,10 @@ public void run() { PreparedStatement d15 = connDomestic.prepareStatement("INSERT INTO DATASHORTTEXT (StatementId, VariableId, Entity) VALUES (?, ?, ?);"); PreparedStatement f15 = connForeign.prepareStatement("SELECT COUNT(ID) FROM STATEMENTS WHERE DocumentId = ?;"); PreparedStatement d16 = connDomestic.prepareStatement("INSERT INTO ATTRIBUTEVARIABLES (VariableId, AttributeVariable) VALUES (?, ?);", PreparedStatement.RETURN_GENERATED_KEYS); - PreparedStatement d17 = connDomestic.prepareStatement("INSERT INTO ATTRIBUTEVALUES (EntityId, AttributeVariableId, AttributeValue) VALUES (?, ?, ?);"); + PreparedStatement d17 = connDomestic.prepareStatement(d17sql); PreparedStatement d18 = connDomestic.prepareStatement("SELECT * FROM ATTRIBUTEVARIABLES WHERE VariableId = ?;"); PreparedStatement d19 = connDomestic.prepareStatement("SELECT ID FROM ATTRIBUTEVARIABLES WHERE VariableId = ? AND AttributeVariable = ?;"); - PreparedStatement d20 = connDomestic.prepareStatement("INSERT INTO ENTITIES (VariableId, Value, Red, Green, Blue) VALUES (?, ?, ?, ?, ?);", PreparedStatement.RETURN_GENERATED_KEYS); + PreparedStatement d20 = connDomestic.prepareStatement(d20sql, PreparedStatement.RETURN_GENERATED_KEYS); PreparedStatement d21 = connDomestic.prepareStatement("SELECT ID FROM ENTITIES WHERE VariableId = ? AND Value = ?;"); PreparedStatement d22 = connDomestic.prepareStatement("UPDATE ATTRIBUTEVALUES SET AttributeValue = ? WHERE EntityId = ? AND AttributeVariableId = ?;"); PreparedStatement d23 = connDomestic.prepareStatement("SELECT AttributeValue FROM ATTRIBUTEVALUES WHERE EntityId = ? AND AttributeVariableId = ?;"); @@ -1262,10 +1275,10 @@ public void run() { int entityId = -1; while (keySetEntity.next()) { entityId = keySetEntity.getInt(1); + entityMap.put(r2.getInt("ID"), entityId); // may not be valid with DNA 2.0 because there is no entity ID; works only with version 3 + entityCount++; } keySetEntity.close(); - entityMap.put(r2.getInt("ID"), entityId); // may not be valid with DNA 2.0 because there is no entity ID; works only with version 3 - entityCount++; } // import attributes for DNA 2.0 here already because they are part of ResultSet r2 anyway @@ -1426,9 +1439,9 @@ public void run() { int entityId = -1; while (keySetEntity.next()) { entityId = keySetEntity.getInt(1); + entityMap.put(r2.getInt("ID"), entityId); // may not be valid with DNA 2.0 because there is no entity ID + entityCount++; } - entityMap.put(r2.getInt("ID"), entityId); // may not be valid with DNA 2.0 because there is no entity ID - entityCount++; } // import attributes for DNA 2.0 here already because they are part of ResultSet r2 anyway From 3d6bd053a899884660d4bdb34ef8044e64694fe9 Mon Sep 17 00:00:00 2001 From: leifeld Date: Sat, 10 Feb 2024 10:05:35 +0000 Subject: [PATCH 09/43] Fixed as.POSIXct origin bug in phase transitions functions --- rDNA/rDNA/DESCRIPTION | 2 +- rDNA/rDNA/R/rDNA.R | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 504ace0b..22068348 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-02-04 +Date: 2024-02-10 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 8703bb2e..bf5d6848 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -4882,7 +4882,7 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", # temporal embedding via MDS if (!requireNamespace("MASS", quietly = TRUE)) { - mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), "state" = l[[best]]$memberships) results$states <- mem warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") @@ -4892,7 +4892,7 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", distmat <- distance_mat + 1e-12 mds <- MASS::isoMDS(distmat) # MDS of distance matrix points <- mds$points - mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), "state" = l[[best]]$memberships, "X1" = points[, 1], "X2" = points[, 2]) @@ -5084,7 +5084,7 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", # retrieve mid-point dates (gamma) m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object - dates <- sapply(m, function(x) as.POSIXct(.jcall(x, "J", "getDateTimeLong"), origin = "1970-01-01")) + dates <- sapply(m, function(x) .jcall(x, "J", "getDateTimeLong")) # long integers, still needs conversion to date # define clustering function hclustMethods <- c("single", "average", "complete", "ward") @@ -5216,7 +5216,7 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", # temporal embedding via MDS if (!requireNamespace("MASS", quietly = TRUE)) { - mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), "state" = l[[best]]$memberships) results$states <- mem warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") @@ -5226,7 +5226,7 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", distmat <- distance_mat + 1e-12 mds <- MASS::isoMDS(distmat) # MDS of distance matrix points <- mds$points - mem <- data.frame("date" = as.POSIXct(dates, format = "%d-%m-%Y", tz = "UTC"), + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), "state" = l[[best]]$memberships, "X1" = points[, 1], "X2" = points[, 2]) From 91eb8840da3ce5c9d2bfb2c7b9072df6f192ba16 Mon Sep 17 00:00:00 2001 From: leifeld Date: Sat, 10 Feb 2024 12:23:58 +0000 Subject: [PATCH 10/43] Efficiency gains in kernel smoothing --- build/bibliography.md | 2 +- dna/src/main/java/dna/Dna.java | 2 +- dna/src/main/java/export/Exporter.java | 47 +++++++++++++++----------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/build/bibliography.md b/build/bibliography.md index b500ceb6..0f673fb2 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-02-04 +date: 2024-02-10 title: "Discourse Network Analysis: Bibliography" --- diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 47532b3a..4d61d6b7 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-02-04"; + public static final String date = "2024-02-10"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index 675037d9..e7bb5b62 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -33,6 +33,7 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.WeekFields; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.DoubleStream; @@ -1923,15 +1924,17 @@ private Matrix processTimeSlice(Matrix matrixResult, ArrayList[ for (int j = 0; j < X[0].length; j++) { for (int k = 0; k < X[0][0].length; k++) { for (int t = 0; t < X[i][j][k].size(); t++) { - if (Exporter.this.qualifierAggregation.equals("ignore")) { - m[i][j] = m[i][j] + zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); - } else if (Exporter.this.qualifierAggregation.equals("subtract")) { - if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("boolean")) { - m[i][j] = m[i][j] + (((double) k) - 0.5) * 2 * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); - } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("integer")) { - m[i][j] = m[i][j] + k * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); - } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("short text")) { + if (Exporter.this.kernel.equals("gaussian") || (!X[i][j][k].get(t).getDateTime().isBefore(matrixResult.getStart()) && !X[i][j][k].get(t).getDateTime().isAfter(matrixResult.getStop()))) { // for computational efficiency, don't include statements outside of temporal bandwidth in computations if not necessary + if (Exporter.this.qualifierAggregation.equals("ignore")) { m[i][j] = m[i][j] + zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.qualifierAggregation.equals("subtract")) { + if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("boolean")) { + m[i][j] = m[i][j] + (((double) k) - 0.5) * 2 * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("integer")) { + m[i][j] = m[i][j] + k * zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } else if (Exporter.this.dataTypes.get(Exporter.this.qualifier).equals("short text")) { + m[i][j] = m[i][j] + zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + } } } } @@ -1956,18 +1959,22 @@ private Matrix processTimeSlice(Matrix matrixResult, ArrayList[ } double qdiff = 1.0 - qsim; for (int t = 0; t < X[i][j][k].size(); t++) { - double z1 = zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); - for (int t2 = 0; t2 < X[i2][j][k2].size(); t2++) { - double z2 = zeta(X[i2][j][k2].get(t2).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); - double z = Math.sqrt(z1 * z2); - if (Exporter.this.qualifierAggregation.equals("congruence")) { - m[i][i2] = m[i][i2] + qsim * z; - } else if (Exporter.this.qualifierAggregation.equals("conflict")) { - m[i][i2] = m[i][i2] + qdiff * z; - } else if (Exporter.this.qualifierAggregation.equals("subtract")) { - m[i][i2] = m[i][i2] + qsim * z - qdiff * z; - } else if (Exporter.this.qualifierAggregation.equals("ignore")) { - m[i][i2] = m[i][i2] + z; + if (Exporter.this.kernel.equals("gaussian") || (!X[i][j][k].get(t).getDateTime().isBefore(matrixResult.getStart()) && !X[i][j][k].get(t).getDateTime().isAfter(matrixResult.getStop()))) { // for computational efficiency, don't include statements outside of temporal bandwidth in computations if not necessary + double z1 = zeta(X[i][j][k].get(t).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + for (int t2 = 0; t2 < X[i2][j][k2].size(); t2++) { + if (Exporter.this.kernel.equals("gaussian") || (!X[i2][j][k2].get(t2).getDateTime().isBefore(matrixResult.getStart()) && !X[i2][j][k2].get(t2).getDateTime().isAfter(matrixResult.getStop()))) { // for computational efficiency, don't include statements outside of temporal bandwidth in computations if not necessary + double z2 = zeta(X[i2][j][k2].get(t2).getDateTime(), matrixResult.getDateTime(), Exporter.this.windowSize, Exporter.this.timeWindow, Exporter.this.kernel); + double z = Math.sqrt(z1 * z2); + if (Exporter.this.qualifierAggregation.equals("congruence")) { + m[i][i2] = m[i][i2] + qsim * z; + } else if (Exporter.this.qualifierAggregation.equals("conflict")) { + m[i][i2] = m[i][i2] + qdiff * z; + } else if (Exporter.this.qualifierAggregation.equals("subtract")) { + m[i][i2] = m[i][i2] + qsim * z - qdiff * z; + } else if (Exporter.this.qualifierAggregation.equals("ignore")) { + m[i][i2] = m[i][i2] + z; + } + } } } } From 617574214f012c069c85bb32c2fa7afe0574beba Mon Sep 17 00:00:00 2001 From: leifeld Date: Tue, 13 Feb 2024 18:18:47 +0000 Subject: [PATCH 11/43] Letting phase transition time points start later and end sooner for more comparable networks --- build/bibliography.md | 2 +- dna/src/main/java/dna/Dna.java | 2 +- dna/src/main/java/export/Exporter.java | 105 ++++++++++++++++--------- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/build/bibliography.md b/build/bibliography.md index 0f673fb2..1648a153 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-02-10 +date: 2024-02-13 title: "Discourse Network Analysis: Bibliography" --- diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 4d61d6b7..2b19ea3a 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-02-10"; + public static final String date = "2024-02-13"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index e7bb5b62..47b3f885 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -33,7 +33,6 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.WeekFields; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.DoubleStream; @@ -110,12 +109,28 @@ public class Exporter { private SimulatedAnnealingBackboneResult simulatedAnnealingBackboneResult = null; // time smoothing + /** + * Kernel function used for time slice network smoothing. Can be {@code "no"} (for no kernel function; uses legacy + * code instead); {@code "uniform"} (for uniform kernel, which is similar to "no"); {@code "epanechnikov"} (for the + * Epanechnikov kernel function; @code "triangular"} (for the triangular kernel function); or {@code "gaussian"} + * (for the Gaussian, or standard normal, kernel function). + */ private String kernel = "no"; + /** + * For kernel-smoothed time slices, should the first mid-point be half a bandwidth or time window duration after the + * start date and the last mid-point be half a bandwidth or duration before the last date to allow sufficient data + * around the end points of the timeline? + */ + private boolean indentBandwidth = true; public void setKernelFunction(String kernel) { this.kernel = kernel; } + public void setIndentBandwidth(boolean indentBandwidth) { + this.indentBandwidth = indentBandwidth; + } + /** *

Create a new Exporter class instance, holding an array list of export * statements (i.e., statements with added document information and a hash @@ -1716,10 +1731,34 @@ public void computeKernelSmoothedTimeSlices() { if (this.windowSize % 2 != 0) { // windowSize is the w constant in the paper; only even numbers are acceptable because adding or subtracting w / 2 to or from gamma would not yield integers this.windowSize = this.windowSize + 1; } + LocalDateTime firstDate = Exporter.this.filteredStatements.get(0).getDateTime(); + LocalDateTime lastDate = Exporter.this.filteredStatements.get(Exporter.this.filteredStatements.size() - 1).getDateTime(); final int W_HALF = windowSize / 2; - LocalDateTime b = this.startDateTime; // start of statement list - LocalDateTime e = this.stopDateTime; // end of statement list + LocalDateTime b = this.startDateTime.isBefore(firstDate) ? firstDate : this.startDateTime; // start of statement list + LocalDateTime e = this.stopDateTime.isAfter(lastDate) ? lastDate : this.stopDateTime; // end of statement list LocalDateTime gamma = b; // current time while progressing through list of statements + LocalDateTime e2 = e; // indented end point (e minus half w) + if (Exporter.this.indentBandwidth) { + if (timeWindow.equals("minutes")) { + gamma = gamma.plusMinutes(W_HALF); + e2 = e.minusMinutes(W_HALF); + } else if (timeWindow.equals("hours")) { + gamma = gamma.plusHours(W_HALF); + e2 = e.minusHours(W_HALF); + } else if (timeWindow.equals("days")) { + gamma = gamma.plusDays(W_HALF); + e2 = e.minusDays(W_HALF); + } else if (timeWindow.equals("weeks")) { + gamma = gamma.plusWeeks(W_HALF); + e2 = e.minusWeeks(W_HALF); + } else if (timeWindow.equals("months")) { + gamma = gamma.plusMonths(W_HALF); + e2 = e.minusMonths(W_HALF); + } else if (timeWindow.equals("years")) { + gamma = gamma.plusYears(W_HALF); + e2 = e.minusYears(W_HALF); + } + } // save the labels of the variables and qualifier and put indices in hash maps for fast retrieval String[] var1Values = extractLabels(Exporter.this.filteredStatements, Exporter.this.variable1, Exporter.this.variable1Document); @@ -1750,78 +1789,66 @@ public void computeKernelSmoothedTimeSlices() { Exporter.this.matrixResults = new ArrayList<>(); if (Exporter.this.kernel.equals("gaussian")) { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by the start and end of the whole time range; the actual matrix is injected later if (timeWindow.equals("minutes")) { - gamma = gamma.minusMinutes(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusMinutes(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusMinutes(1); } } else if (timeWindow.equals("hours")) { - gamma = gamma.minusHours(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusHours(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusHours(1); } } else if (timeWindow.equals("days")) { - gamma = gamma.minusDays(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusDays(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusDays(1); } } else if (timeWindow.equals("weeks")) { - gamma = gamma.minusWeeks(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusWeeks(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusWeeks(1); } } else if (timeWindow.equals("months")) { - gamma = gamma.minusMonths(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusMonths(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusMonths(1); } } else if (timeWindow.equals("years")) { - gamma = gamma.minusYears(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusYears(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, b, gamma, e)); + gamma = gamma.plusYears(1); } } } else { // for each mid-point gamma, create an empty Matrix and save the start, mid, and end time points in it as defined by width w; the actual matrix is injected later if (timeWindow.equals("minutes")) { - gamma = gamma.minusMinutes(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusMinutes(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMinutes(W_HALF).isBefore(b) ? b : gamma.minusMinutes(W_HALF), gamma, gamma.plusMinutes(W_HALF).isAfter(e) ? e : gamma.plusMinutes(W_HALF))); + gamma = gamma.plusMinutes(1); } } else if (timeWindow.equals("hours")) { - gamma = gamma.minusHours(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusHours(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusHours(W_HALF).isBefore(b) ? b : gamma.minusHours(W_HALF), gamma, gamma.plusHours(W_HALF).isAfter(e) ? e : gamma.plusHours(W_HALF))); + gamma = gamma.plusHours(1); } } else if (timeWindow.equals("days")) { - gamma = gamma.minusDays(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusDays(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusDays(W_HALF).isBefore(b) ? b : gamma.minusDays(W_HALF), gamma, gamma.plusDays(W_HALF).isAfter(e) ? e : gamma.plusDays(W_HALF))); + gamma = gamma.plusDays(1); } } else if (timeWindow.equals("weeks")) { - gamma = gamma.minusWeeks(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusWeeks(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusWeeks(W_HALF).isBefore(b) ? b : gamma.minusWeeks(W_HALF), gamma, gamma.plusWeeks(W_HALF).isAfter(e) ? e : gamma.plusWeeks(W_HALF))); + gamma = gamma.plusWeeks(1); } } else if (timeWindow.equals("months")) { - gamma = gamma.minusMonths(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusMonths(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusMonths(W_HALF).isBefore(b) ? b : gamma.minusMonths(W_HALF), gamma, gamma.plusMonths(W_HALF).isAfter(e) ? e : gamma.plusMonths(W_HALF))); + gamma = gamma.plusMonths(1); } } else if (timeWindow.equals("years")) { - gamma = gamma.minusYears(1); - while (gamma.isBefore(e)) { - gamma = gamma.plusYears(1); + while (!gamma.isAfter(e2)) { Exporter.this.matrixResults.add(new Matrix(var1Values, Exporter.this.networkType.equals("onemode") ? var1Values : var2Values, false, gamma.minusYears(W_HALF).isBefore(b) ? b : gamma.minusYears(W_HALF), gamma, gamma.plusYears(W_HALF).isAfter(e) ? e : gamma.plusYears(W_HALF))); + gamma = gamma.plusYears(1); } } } From a5b5381b02de86b48756b2f289a9b4d3214fe9f7 Mon Sep 17 00:00:00 2001 From: leifeld Date: Wed, 28 Feb 2024 12:40:43 +0000 Subject: [PATCH 12/43] Updated bibliography --- bibliography/bibliography.bib | 76 +++- bibliography/build.gradle | 12 + build/bibliography.md | 659 ++++++++++++++++++---------------- 3 files changed, 426 insertions(+), 321 deletions(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index 63954779..f7219459 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -86,6 +86,15 @@ @article{aryal2022navigating doi={10.1016/j.forpol.2022.102768} } +@article{baekgaard2024technocentric, + title={The Technocentric Consensus: A Discourse Network Analysis of the {E}uropean Circular Economy Debate}, + author={B{\ae}kgaard, Aksel S. and Engberg, Christian and Hasselbalch, Jacob A.}, + journal={Journal of Environmental Policy \& Planning}, + year={2024}, + note={Forthcoming}, + doi={10.1080/1523908X.2024.2317793} +} + @mastersthesis{bandau2021emerging, title={Emerging Institutions in the Global Space Sector. {A}n Institutional Logics Approach}, author={Bandau, Sanja Lisa}, @@ -385,6 +394,14 @@ @phdthesis{ciordia2020less doi={10.15168/11572_277816} } +@article{clasing2024free, + title={How Free Tuition Cecame a Policy in {C}hile: The Importance of Policy Actors and their Beliefs}, + author={Clasing-Manquian, Paula}, + journal={Higher Education}, + year={2024}, + note={Forthcoming} +} + @mastersthesis{coronado2015reforma, title={La Reforma Tributaria 2014: Un an{\'a}lisis desde las coaliciones discursivas}, author={Coronado Vigueras, Rodrigo Alonso}, @@ -420,9 +437,8 @@ @article{demler2023discourse author={Demler, Katja and Thurm, Stefanie}, year={2023}, journal={{G}erman Politics}, - volume={33}, note={Forthcoming}, - doi={10.1016/j.eist.2019.02.002} + doi={10.1080/09644008.2023.2288021} } @article{diaz2018competing, @@ -445,6 +461,15 @@ @mastersthesis{Drozdzynski2022common url={https://purl.utwente.nl/essays/92422} } +@article{durel2024border, + title={Border Carbon Adjustment Compliance and the {WTO}: The Interactional Evolution of Law}, + author={Durel, Laurie}, + journal={Journal of International Economic Law}, + year={2024}, + doi={10.1093/jiel/jgae007}, + note={Forthcoming} +} + @phdthesis{duygan2018actor, title={An Actor-Based Analysis of Political Context for Supporting Sustainability Transitions of Socio-Technical Systems: A Study of {S}wiss Waste Management}, author={Duygan, Mert}, @@ -1357,8 +1382,10 @@ @article{kukkonen2023cultural title={A Cultural Approach to Politicization of Science: {H}ow the Forestry Coalition Challenged the Scientific Consensus in the {F}innish News Media Debate on Increased Logging}, journal={Society \& Natural Resources}, year={2023}, - doi={10.1080/08941920.2023.2259326}, - note={Forthcoming} + volume={37}, + number={1}, + pages={91--112}, + doi={10.1080/08941920.2023.2259326} } @article{kukkonen2020science, @@ -1529,6 +1556,7 @@ @mastersthesis{leipold2016oekonomische @article{lemke2023defining, title={Agenda-setting in Nascent Policy Subsystems: {I}ssue and Instrument Priorities across Venues}, author={Lemke, Nicole and Trein, Philipp and Varone, Fr{\'e}d{\'e}ric}, + journal={Policy Sciences}, year={2023}, volume={56}, pages={633--655}, @@ -1813,7 +1841,6 @@ @article{murti2022playground journal={Journal of Communication Inquiry}, note={Forthcoming}, year={2022}, - publisher={SAGE Publications Sage CA: Los Angeles, CA}, doi={10.1177/01968599211072438} } @@ -1879,8 +1906,9 @@ @article{nagel2023powerful author={Nagel, Melanie and Sch{\"a}fer, Melanie}, journal={Review of Policy Research}, year={2023}, - publisher={Wiley Online Library}, - note={Forthcoming}, + volume={40}, + number={6}, + pages={1093--1119}, doi={10.1111/ropr.12545} } @@ -2257,7 +2285,6 @@ @article{rone2022instrumentalising title={Instrumentalising Sovereignty Claims in {B}ritish Pro- and Anti-{B}rexit Mobilisations}, author={Rone, Julia}, journal={The British Journal of Politics and International Relations}, - note={Forthcoming}, year={2023}, volume={25}, number={3}, @@ -2523,7 +2550,9 @@ @article{shukla2022energytransition author={Shukla, Rajshri and Swarnakar, Pradip}, year={2022}, journal={Globalizations}, - note={Forthcoming}, + volume={20}, + number={8}, + pages={1296--1311}, doi={10.1080/14747731.2022.2086735} } @@ -2543,9 +2572,9 @@ @article{sick2023rhetoric author={Sick, Harald}, journal={Interest Groups \& Advocacy}, year={2023}, - publisher={Springer}, - doi={10.1057/s41309-023-00195-2}, - note={Forthcoming} + volume={12}, + pages={388--412}, + doi={10.1057/s41309-023-00195-2} } @article{silalahi2023analisis, @@ -2726,8 +2755,9 @@ @article{stoddart2022roles author={Stoddart, Mark C. J. and Yang, Yixi}, journal={Review of Policy Research}, year={2022}, - publisher={Wiley Online Library}, - note={Forthcoming}, + volume={40}, + number={6}, + pages={1144--1168}, doi={10.1111/ropr.12510} } @@ -2959,6 +2989,16 @@ @article{vogeler2021agri doi={10.1002/epa2.1114} } +@article{volker2024discourse, + title={Discourse Networks of the Far Right: How Far-Right Actors Become Mainstream in Public Debates}, + author={V{\"o}lker, Teresa and Saldivia Gonzatti, Daniel}, + journal={Political Communication}, + pages={1--20}, + year={2024}, + doi={10.1080/10584609.2024.2308601}, + note={Forthcoming} +} + @article{von2021plenardebatten, title={Plenardebatten als {S}piegel sich wandelnder {D}iskurskoalitionen: Die {P}ositionierung der {B}undestagsfraktionen zum {V}erh{\"a}ltnis von {\"O}kologie und {\"O}konomie seit 1977}, author={von Steinsdorff, Silvia and Gottmann, Lennard and H{\"u}ggelmeyer, Malte and Jeske, Ines-Maria and Onkelbach, Charlotte and Siebeking, Johanna}, @@ -2971,6 +3011,14 @@ @article{von2021plenardebatten doi={10.5771/0340-1758-2021-3-640} } +@mastersthesis{wagemans2023shadow, + title={A Shadow on Solar Mobility: The Impact of Failure on Socio-Technical System Configurations}, + author={Wagemans, Sander}, + year={2023}, + school={Universiteit Utrecht, Copernicus Institute of Sustainable Development}, + doi={20.500.12932/45910} +} + @article{wagner2017trends, title={Trends, Frames and Discourse Networks: Analysing the Coverage of Climate Change in {I}rish Newspapers}, author={Wagner, Paul and Payne, Diane}, diff --git a/bibliography/build.gradle b/bibliography/build.gradle index 421680e1..537c685d 100644 --- a/bibliography/build.gradle +++ b/bibliography/build.gradle @@ -22,8 +22,20 @@ task bibliographyPdflatex { exec { workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" + } + + exec { + workingDir 'temp' commandLine 'bibtex', "bibliography" + } + + exec { + workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" + } + + exec { + workingDir 'temp' commandLine 'pdflatex', "bibliography.tex" } diff --git a/build/bibliography.md b/build/bibliography.md index 1648a153..058cb510 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-02-13 +date: 2024-02-28 title: "Discourse Network Analysis: Bibliography" --- @@ -97,9 +97,18 @@ scientific forest management. *Forest Policy and Economics*, *141*,

+
+ +9\. BĂŠkgaard, A. S., Engberg, C., & Hasselbalch, J. A. (2024). The +technocentric consensus: A discourse network analysis of the European +circular economy debate. *Journal of Environmental Policy & Planning*. + + +
+
-9\. Bandau, S. L. (2021). *Emerging institutions in the global space +10\. Bandau, S. L. (2021). *Emerging institutions in the global space sector. An institutional logics approach* \[Master's Thesis, Utrecht University, Copernicus Institute of Sustainable Development\]. @@ -108,7 +117,7 @@ University, Copernicus Institute of Sustainable Development\].
-10\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them +11\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them all? Narrating the agenda for labor market policies in France and Germany. *Policy and Society*, *38*(3), 408–428. @@ -117,7 +126,7 @@ Germany. *Policy and Society*, *38*(3), 408–428.
-11\. Barnickel, C. (2019). *Postdemokratisierung der +12\. Barnickel, C. (2019). *Postdemokratisierung der Legitimationspolitik: Diskursnetzwerke in bundesdeutschen Großen RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS. @@ -126,7 +135,7 @@ RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS.
-12\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: +13\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: Legitimationspolitik in der Großen RegierungserklĂ€rung der 19. Wahlperiode im Vergleich. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *14*(3), 199–223. @@ -136,7 +145,7 @@ Politikwissenschaft*, *14*(3), 199–223.
-13\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and +14\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and management integration in Germany, Spain and Sweden. *Land Use Policy*, *101*, 105182. @@ -144,7 +153,7 @@ management integration in Germany, Spain and Sweden. *Land Use Policy*,
-14\. Belova, A. (2021). *Another silver bullet for the energy +15\. Belova, A. (2021). *Another silver bullet for the energy transition? Discourse network analysis of German hydrogen debate* \[Master’s thesis, Department of International Relations; European Studies, Energy Policy Studies, Masaryk University\]. @@ -154,7 +163,7 @@ Studies, Energy Policy Studies, Masaryk University\].
-15\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & +16\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & Kemmerzell, J. (2023). The more the merrier? Actors and ideas in the evolution of German hydrogen policy discourse. *Energy Research & Social Science*, *97*, 102965. @@ -163,7 +172,7 @@ Science*, *97*, 102965.
-16\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse +17\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse network analysis as a novel approach to party control. *Politics and Governance*, *8*(2), 229–242. @@ -171,7 +180,7 @@ Governance*, *8*(2), 229–242.
-17\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a +18\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a tool to maintain party unity: The case of oppressed political conflict in German parliament debates on the Euro crisis. *Party Politics*, *29*(3), 554--569. @@ -180,7 +189,7 @@ in German parliament debates on the Euro crisis. *Party Politics*,
-18\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network +19\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network analysis of climate delayism in the Czech Republic* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. @@ -189,7 +198,7 @@ Masaryk University, Faculty of Social Studies\].
-19\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & +20\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & Padó, S. (2019). An environment for relational annotation of political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations*, 105–110. @@ -199,7 +208,7 @@ Computational Linguistics: System Demonstrations*, 105–110.
-20\. Boas, I. (2015). *Climate migration and security: Securitisation as +21\. Boas, I. (2015). *Climate migration and security: Securitisation as a strategy in climate change politics*. Routledge. @@ -207,7 +216,7 @@ a strategy in climate change politics*. Routledge.
-21\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice +22\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice o energetickej efektĂ­vnosti – analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -216,7 +225,7 @@ Thesis, Masaryk University, Faculty of Social Studies\].
-22\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual +23\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual screening: Analyzing roles, content and motivations in political Twitter conversations. *Politics and Governance*, *8*(2), 311–325. @@ -225,7 +234,7 @@ conversations. *Politics and Governance*, *8*(2), 311–325.
-23\. Brandenberger, L. (2019). Predicting network events to assess +24\. Brandenberger, L. (2019). Predicting network events to assess goodness of fit of relational event models. *Political Analysis*, *27*(4), 556–571. @@ -233,7 +242,7 @@ goodness of fit of relational event models. *Political Analysis*,
-24\. Breindl, Y. (2013). Discourse networks on state-mandated access +25\. Breindl, Y. (2013). Discourse networks on state-mandated access blocking in Germany and France. *Info*, *15*(6), 42–62. @@ -241,7 +250,7 @@ blocking in Germany and France. *Info*, *15*(6), 42–62.
-25\. Bresser, B. (2024). *Transitions in the risk assessment regime: +26\. Bresser, B. (2024). *Transitions in the risk assessment regime: Prospects from the case of animal-free testing in the cosmetics industry* \[Master's Thesis, Utrecht University, Innovation Sciences\]. @@ -250,7 +259,7 @@ industry* \[Master's Thesis, Utrecht University, Innovation Sciences\].
-26\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, +27\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, A., Chien, Y.-J., Edling, C., Fisher, D. R., Giouzepas, G., Haluza-DeLay, R., Hazegawa, K., Hirschi, C., Horta, A., Ikeda, K., Jin, J., Ku, D., Lahsen, M., Lee, H.-C., Lin, T.-L. A., 
 Zhengyi, S. (2016). @@ -262,7 +271,7 @@ Conflicting climate change frames in a global field of media discourse.
-27\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis +28\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis of climate change and society: A network approach. In M. J. Manfredo, J. J. Vaske, A. Rechkemmer, & E. A. Duke (Eds.), *Understanding society and natural resources. Forging new strands of integration across the social @@ -273,7 +282,7 @@ sciences* (pp. 203–228). Springer Open.
-28\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: +29\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: Making sense of the international discourse on transfer pricing methodologies. *Review of International Political Economy*, *29*(1), 307–335. @@ -282,7 +291,7 @@ methodologies. *Review of International Political Economy*, *29*(1),
-29\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse +30\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse networks on local energy transitions. *Environmental Innovation and Societal Transitions*, *39*, 141–154. @@ -291,7 +300,7 @@ Societal Transitions*, *39*, 141–154.
-30\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die +31\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die US-Außenpolitik. Ein Vergleich der Israelpolitik zwischen den Kabinetten Obama II und Trump* \[Master's Thesis, Leopold-Franzens-UniversitĂ€t Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\]. @@ -301,7 +310,7 @@ Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\].
-31\. Brutschin, E. (2013). *Dynamics in EU policy-making: The +32\. Brutschin, E. (2013). *Dynamics in EU policy-making: The liberalization of the European gas market* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -310,7 +319,7 @@ University of Konstanz, Department of Politics; Public Administration\].
-32\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A +33\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A discourse network analysis of UK newspaper coverage of the “sugar tax” debate before and after the announcement of the soft drinks industry levy. *BMC Public Health*, *19*(490), 1–14. @@ -320,7 +329,7 @@ levy. *BMC Public Health*, *19*(490), 1–14.
-33\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of +34\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of territoriality in policy networks. The case of transportation policies in the cross-border metropolitan regions of Brussels and Luxembourg* \[Doctoral Dissertation, KU Leuven, Division of Geography; Tourism\]. @@ -330,7 +339,7 @@ in the cross-border metropolitan regions of Brussels and Luxembourg*
-34\. Černỳ, O. (2018). *Limity českĂ© +35\. Černỳ, O. (2018). *Limity českĂ© energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -339,7 +348,7 @@ energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­*
-35\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media +36\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media coverage: A case of the Czech coal policy. *Politics and Governance*, *8*(2), 272–285. @@ -347,7 +356,7 @@ coverage: A case of the Czech coal policy. *Politics and Governance*,
-36\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej +37\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej politike Českej republiky* \[Master's Thesis, Masarykova univerzita, Fakulta sociĂĄlnı́ch studiı́\]. @@ -355,16 +364,24 @@ Fakulta sociĂĄlnı́ch studiı́\].
-37\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green +38\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green networks in the Basque country between 2007 and 2017* \[PhD thesis, University of Trento, School of Social Sciences\].
+
+ +39\. Clasing-Manquian, P. (2024). How free tuition cecame a policy in +Chile: The importance of policy actors and their beliefs. *Higher +Education*. + +
+
-38\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un +40\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un anĂĄlisis desde las coaliciones discursivas* \[Tesis Postgrado, Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\]. @@ -373,7 +390,7 @@ Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\].
-39\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking +41\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking in unison? Explaining the role of agenda-setter constellations in the ECB policy agenda using a network-based approach. *Journal of European Public Policy*. @@ -382,7 +399,7 @@ Public Policy*.
-40\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). +42\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). Depicting mangrove’s potential as blue carbon champion in indonesia. In R. Dasgupta, S. Hashimoto, & O. Saito (Eds.), *Assessing, mapping and modelling of mangrove ecosystem services in the asia-pacific region* @@ -392,16 +409,16 @@ modelling of mangrove ecosystem services in the asia-pacific region*
-41\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse +43\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse network analysis of the political debate about the German passenger car -toll act. *German Politics*, *33*. - +toll act. *German Politics*. +
-42\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the +44\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the climate change arena in Mexico: A network analysis. *Journal of Environmental Management*, *215*, 239–247. @@ -410,16 +427,24 @@ Environmental Management*, *215*, 239–247.
-43\. Drozdzynski, F. A. (2022). *The common agricultural policy post +45\. Drozdzynski, F. A. (2022). *The common agricultural policy post 2020: An analysis of the beliefs of selected key stakeholders* \[Master's Thesis, University of Twente, Faculty of Behavioural, Management; Social Sciences\].
+
+ +46\. Durel, L. (2024). Border carbon adjustment compliance and the WTO: +The interactional evolution of law. *Journal of International Economic +Law*. + +
+
-44\. Duygan, M. (2018). *An actor-based analysis of political context +47\. Duygan, M. (2018). *An actor-based analysis of political context for supporting sustainability transitions of socio-technical systems: A study of Swiss waste management* \[Doctoral Dissertation, ETH ZĂŒrich, Department of Environmental Systems Science\]. @@ -429,7 +454,7 @@ Department of Environmental Systems Science\].
-45\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for +48\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for conceptualizing and uncovering the determinants of agency in socio-technical transitions. *Environmental Innovation and Societal Transitions*, *33*, 13–29. @@ -438,7 +463,7 @@ Transitions*, *33*, 13–29.
-46\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse +49\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse coalitions in Swiss waste management: Gridlock or winds of change? *Waste Management*, *72*, 25–44. @@ -447,7 +472,7 @@ coalitions in Swiss waste management: Gridlock or winds of change?
-47\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes +50\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes agency? Determinants of actors’ influence on formal institutions in Swiss waste management. *Technological Forecasting and Social Change*, *162*, 120413. @@ -456,7 +481,7 @@ Swiss waste management. *Technological Forecasting and Social Change*,
-48\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How +51\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How discourse network analysis (DNA) can help CSR research to investigate the “new” political role of corporations. In M. Nagel, P. Kenis, P. Leifeld, & H.-J. Schmedes (Eds.), *Politische komplexitĂ€t, governance @@ -467,14 +492,14 @@ von innovationen und policy-netzwerke* (pp. 139–146). Springer VS.
-49\. Eder, F. (2015). *Der irakkrieg 2003*. Innsbruck University Press. +52\. Eder, F. (2015). *Der irakkrieg 2003*. Innsbruck University Press.
-50\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. +53\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. Ostermann (Eds.), *Routledge handbook of foreign policy analysis methods* (pp. 516–535). Taylor & Francis. @@ -483,7 +508,7 @@ methods* (pp. 516–535). Taylor & Francis.
-51\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, +54\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*, *15*(1), 21–42. @@ -491,7 +516,7 @@ discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*,
-52\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting +55\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting counter-terrorism: Discourse networks and the politicisation of counter-terrorism in Austria. *Journal of International Relations and Development*, *24*(1), 171–195. @@ -501,7 +526,7 @@ Development*, *24*(1), 171–195.
-53\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan +56\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan komunikator publik dalam berita omicron baru di media online. *Jurnal Riset Komunikasi*, *6*(1), 58–79. @@ -510,7 +535,7 @@ Riset Komunikasi*, *6*(1), 58–79.
-54\. Elislah, N. (2023). Discourse network analysis on delaying +57\. Elislah, N. (2023). Discourse network analysis on delaying elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2), 225–240. @@ -518,7 +543,7 @@ elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2),
-55\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue +58\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue debate: A study on Covid-19 cases in Indonesia. *Jurnal Komunikasi: Malaysian Journal of Communication*, *36*(3), 209–227. @@ -527,7 +552,7 @@ Malaysian Journal of Communication*, *36*(3), 209–227.
-56\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping +59\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping discourse coalitions in the minimum unit pricing for alcohol debate: A discourse network analysis of UK newspaper coverage. *Addiction*, *114*(4), 741–753. @@ -536,7 +561,7 @@ discourse network analysis of UK newspaper coverage. *Addiction*,
-57\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological +60\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological tensions in education policy networks: An analysis of the policy innovators in education network in the United States. *Foro de Educacion*, *19*(1), 11–28. @@ -545,7 +570,7 @@ Educacion*, *19*(1), 11–28.
-58\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban +61\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban food strategies. In C. Tornaghi (Ed.), *Re-imagining sustainable food planning, building resourcefulness: Food movements, insurgent planning and heterodox economics: Proceedings of the 8th annual conference AESOP @@ -556,7 +581,7 @@ https://doi.org/ -59\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate +62\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate policy blockage. *Climatic Change*, *155*(4), 469–487. @@ -564,7 +589,7 @@ policy blockage. *Climatic Change*, *155*(4), 469–487.
-60\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the +63\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the ideological networks of American climate politics. *Climatic Change*, *116*(3), 523–545. @@ -572,7 +597,7 @@ ideological networks of American climate politics. *Climatic Change*,
-61\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does +64\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does political polarization come from? Locating polarization within the US climate change debate. *American Behavioral Scientist*, *57*(1), 70–92. @@ -581,7 +606,7 @@ climate change debate. *American Behavioral Scientist*, *57*(1), 70–92.
-62\. Friis, G. (2020). *Populist radical right parties into parliament: +65\. Friis, G. (2020). *Populist radical right parties into parliament: Changes in mainstream parties’ political positions in parliamentary debates on immigration and refugees* \[Master’s thesis, Uppsala University, Disciplinary Domain of Humanities; Social Sciences, Faculty @@ -592,7 +617,7 @@ of Social Sciences, Department of Government\].
-63\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network +66\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network analysis to understand ideological convergence and change in educational subsystems. *Education Policy Analysis Archives*, *28*(118). @@ -601,7 +626,7 @@ subsystems. *Education Policy Analysis Archives*, *28*(118).
-64\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). +67\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). Building consensus: Idea brokerage in teacher policy networks. *American Educational Research Journal*, *57*(2), 872–905. @@ -610,7 +635,7 @@ Educational Research Journal*, *57*(2), 872–905.
-65\. Galli Robertson, A. M. (2021). Privileged accounts in the debate +68\. Galli Robertson, A. M. (2021). Privileged accounts in the debate over coal-fired power in the United States. *Society & Natural Resources*, *34*(2), 188–207. @@ -619,7 +644,7 @@ Resources*, *34*(2), 188–207.
-66\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable +69\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable country: Agenda-setting and a discourse network approach on Philippine broadsheet media* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -629,7 +654,7 @@ Science, Oeschger Centre for Climate Change Research\].
-67\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The +70\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The politics of climate finance: Consensus and partisanship in designing green state investment banks in the United Kingdom and Australia. *Energy Research & Social Science*, *69*, 101583. @@ -639,7 +664,7 @@ green state investment banks in the United Kingdom and Australia.
-68\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local +71\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local policymakers’ attitudes towards climate change: A multi-method case study. *Social Networks*, *25*, 197–209. @@ -648,7 +673,7 @@ study. *Social Networks*, *25*, 197–209.
-69\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates +72\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates and agricultural policies: Discourse coalitions behind the creation of Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80. @@ -657,7 +682,7 @@ Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80.
-70\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social +73\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social innovation: Analysing policy debates in Japan. *Journal of Social Entrepreneurship*. @@ -665,7 +690,7 @@ Entrepreneurship*.
-71\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate +74\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate change in Italy: A discourse network analysis. *Politics and Governance*, *8*(2), 215–228. @@ -673,7 +698,7 @@ Governance*, *8*(2), 215–228.
-72\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and +75\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and right: A discourse network analysis of universal basic income on Dutch Twitter. *Journal of Social Policy*. @@ -682,7 +707,7 @@ Twitter. *Journal of Social Policy*.
-73\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage +76\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4), 490–514. @@ -690,7 +715,7 @@ in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4),
-74\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of +77\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of cultural resistance to a speed limit on German highways* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -699,7 +724,7 @@ Development\].
-75\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, +78\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, C. (2022). Discourse network analysis of nuclear narratives. In M. D. Jones, M. K. McBeth, & E. Shanahan (Eds.), *Narratives and the policy process: Applications of the narrative policy framework* (pp. 13–38). @@ -709,7 +734,7 @@ Montana State University Library.
-76\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations +79\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations of the mexican power generation policy subsystem. In A.-M. Bercu, I. Bilan, & C.-M. Apostoaie (Eds.), *European administrative area: Integration and resilience dynamics. Proceedings of the international @@ -721,7 +746,7 @@ Ioan Cuza” din Iai.
-77\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y +80\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y decisiones: La polĂ­tica de generaciĂłn elĂ©ctrica en mĂ©xico 1994–2018* \[PhD thesis, Centro de InvestigaciĂłn y Docencia EconĂłmicas (CIDE), Doctorado en PolĂ­ticas PĂșblicas\]. @@ -730,7 +755,7 @@ Doctorado en PolĂ­ticas PĂșblicas\].
-78\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and +81\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and policy change in Mexican electricity policy: A discourse network analysis. *Policy & Politics*. @@ -739,7 +764,7 @@ analysis. *Policy & Politics*.
-79\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on +82\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on the revision of Indonesian information and electronic transaction law. *Jurnal Studi Komunikasi*, *7*(2), 519–538. @@ -748,7 +773,7 @@ the revision of Indonesian information and electronic transaction law.
-80\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics +83\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics after the “great recession.” Investigating the impact of economic shocks on EU climate policy-making in three case studies* \[Doctoral Dissertation, University of Potsdam, Faculty of Economics; Social @@ -758,7 +783,7 @@ Sciences\].
-81\. Hanschmann, R. (2017). Polarized business interests. EU climate +84\. Hanschmann, R. (2017). Polarized business interests. EU climate policy-making during the “great recession.” In D. K. Jesuit & R. A. Williams (Eds.), *Public policy, governance and polarization. Making governance work* (1st ed., pp. 126–156). Routledge. @@ -768,7 +793,7 @@ governance work* (1st ed., pp. 126–156). Routledge.
-82\. Hasselbalch, J. (2017). *The contentious politics of disruptive +85\. Hasselbalch, J. (2017). *The contentious politics of disruptive innovation: Vaping and fracking in the European Union* \[PhD thesis, University of Warwick, Department of Politics; International Studies; Université Libre de Bruxelles, Département de Sciences Politiques\]. @@ -778,7 +803,7 @@ Université Libre de Bruxelles, Département de Sciences Politiques\].
-83\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity +86\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity and skills in European labor mobility. *Review of International Political Economy*, *26*(6), 1333–1360. @@ -787,7 +812,7 @@ Political Economy*, *26*(6), 1333–1360.
-84\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke +87\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau (Eds.), *Capitalism and its legitimacy in times of crisis* (pp. 191–220). Palgrave Macmillan. @@ -797,7 +822,7 @@ without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau
-85\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der +88\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der Atomenergie: Diskursnetzwerkanalyse als Beitrag zur ErklĂ€rung einer radikalen Politikwende. *Zeitschrift fĂŒr Diskursforschung / Journal for Discourse Studies*, *1*(3), 288–316. @@ -807,7 +832,7 @@ Discourse Studies*, *1*(3), 288–316.
-86\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of +89\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of political discourse dynamics and the case of Germany’s nuclear energy phase-out. *Network Science*, *11*(2), 205–223. @@ -816,7 +841,7 @@ phase-out. *Network Science*, *11*(2), 205–223.
-87\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, +90\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, E., & Lapesa, G. (2020). Integrating manual and automatic annotation for the creation of discourse network data sets. *Politics and Governance*, *8*(2), 326–339. @@ -825,7 +850,7 @@ the creation of discourse network data sets. *Politics and Governance*,
-88\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). +91\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). Finanzkrise ohne Legitimationskrise? Kapitalismuskritik in der deutschen QualitĂ€tspresse. In M. Dammayr, D. Grass, & B. RothmĂŒller (Eds.), *LegitimitĂ€t. Gesellschaftliche, politische und wissenschaftliche @@ -836,7 +861,7 @@ Bruchlinien in der Rechtfertigung* (pp. 73–94). Transcript.
-89\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive +92\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive practices and the “need” for positive behaviour support (PBS): A critical discourse examination of disability policy beliefs. *Journal of Intellectual Disabilities*, *27*(1), 170–189. @@ -846,7 +871,7 @@ Intellectual Disabilities*, *27*(1), 170–189.
-90\. Heiberg, J. (2022). *The geography of configurations that work* +93\. Heiberg, J. (2022). *The geography of configurations that work* \[Doctoral Dissertation, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -854,7 +879,7 @@ Sustainable Development\].
-91\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of +94\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of technology legitimation: How multiscalar institutional dynamics matter for path creation in emerging industries. *Economic Geography*, *96*(5), 470–498. @@ -863,7 +888,7 @@ for path creation in emerging industries. *Economic Geography*, *96*(5),
-92\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions +95\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions through socio-technical configuration analysis – a methodological framework and a case study in the water sector. *Research Policy*, *51*(1), 104363. @@ -872,7 +897,7 @@ framework and a case study in the water sector. *Research Policy*,
-93\. Heinmiller, T. B. (2023). Have advocacy coalitions been +96\. Heinmiller, T. B. (2023). Have advocacy coalitions been difference-making in Canadian policy processes? Evidence from firearms policy processes in the 1970s and 1990s. *Canadian Political Science Review*, *17*(2), 1–17. @@ -882,7 +907,7 @@ Review*, *17*(2), 1–17.
-94\. Henrichsen, T. (2020). *Party competition as interdependent process +97\. Henrichsen, T. (2020). *Party competition as interdependent process – assessing the contagion effect of Eurosceptic parties in Italy* \[PhD thesis, Sant’Anna School of Advanced Studies Pisa, Joint PhD in Political Science, European Politics; International Relations\]. @@ -892,7 +917,7 @@ Political Science, European Politics; International Relations\].
-95\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global +98\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global governance: Intersecting politicizations and the reform of investor–state arbitration. *Politics and Governance*, *8*(1), 336–347. @@ -901,7 +926,7 @@ investor–state arbitration. *Politics and Governance*, *8*(1), 336–347.
-96\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in +99\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in der Plattformökonomie. Herausforderungen des “coalition building” bei der Kooperation zwischen IG Metall und YouTubers Union. *Zeitschrift fĂŒr Soziologie*, *51*(2), 174–192. @@ -910,7 +935,7 @@ Soziologie*, *51*(2), 174–192.
-97\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, +100\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, P. (2020). Policy congruence and advocacy strategies in the discourse networks of minimum unit pricing for alcohol and the soft drinks industry levy. *Addiction*, *115*(12), 2303–2314. @@ -920,7 +945,7 @@ industry levy. *Addiction*, *115*(12), 2303–2314.
-98\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing +101\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing states’ messages about common core instruction: An analysis of English/language arts and close reading resources. *Teachers College Record*, *122*(3), 1–42. @@ -929,7 +954,7 @@ Record*, *122*(3), 1–42.
-99\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: +102\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -938,7 +963,7 @@ Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta*
-100\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in +103\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in Canada’s parliamentary system* \[PhD thesis, Simon Fraser University, Department of Political Science\]. @@ -946,7 +971,7 @@ Department of Political Science\].
-101\. Horning, D. G. (2017). *Understanding structure and character in +104\. Horning, D. G. (2017). *Understanding structure and character in rural water governance networks* \[PhD thesis, University of British Columbia, College of Graduate Studies\]. @@ -954,7 +979,7 @@ Columbia, College of Graduate Studies\].
-102\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). +105\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). Programmatisches Handeln in der deutschen Verkehrspolitik. Gemeinsame IdentitĂ€ten von Akteuren im Umfeld des Deutschlandtakts. In D. Sack, H. Straßheim, & K. Zimmermann (Eds.), *Renaissance der Verkehrspolitik. @@ -965,7 +990,7 @@ Springer VS.
-103\. Howe, A. C. (2022). *Network processes related to political +106\. Howe, A. C. (2022). *Network processes related to political discourse and policy positions: The case of climate change policy networks in Canada* \[PhD thesis, University of British Columbia, Department of Sociology\]. @@ -974,7 +999,7 @@ Department of Sociology\].
-104\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media +107\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media coverage and perceived policy influence of environmental actors: Good strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310. @@ -983,7 +1008,7 @@ strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310.
-105\. Hullmann, C. (2023). *Case study on the German discourse of +108\. Hullmann, C. (2023). *Case study on the German discourse of industry decarbonization* \[Master's Thesis, Radboud University Nijmegen, Nijmegen School of Management\]. @@ -992,7 +1017,7 @@ Nijmegen, Nijmegen School of Management\].
-106\. Hurka, S., & Nebel, K. (2013). Framing and policy change after +109\. Hurka, S., & Nebel, K. (2013). Framing and policy change after shooting rampages: A comparative analysis of discourse networks. *Journal of European Public Policy*, *20*(3), 390–406. @@ -1001,7 +1026,7 @@ shooting rampages: A comparative analysis of discourse networks.
-107\. Imbert, I. (2017). *An inquiry into the material and ideational +110\. Imbert, I. (2017). *An inquiry into the material and ideational dimensions of policymaking: A case study of fuel poverty in Germany* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -1011,7 +1036,7 @@ Public Administration\].
-108\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja +111\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja kannatuskoalitiot varhaiskasvatuksen diskurssiverkostossa* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -1020,7 +1045,7 @@ Thesis, University of Helsinki, Faculty of Social Sciences\].
-109\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). +112\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). Diskursnetzwerkanalyse. Überlegungen zur Theoriebildung und Methodik. In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. @@ -1030,7 +1055,7 @@ V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.),
-110\. Jeong, M. (2017). *National renewable energy policy in a global +113\. Jeong, M. (2017). *National renewable energy policy in a global world* \[PhD thesis, University of Maryland, College Park, School of Public Policy\]. @@ -1039,7 +1064,7 @@ Public Policy\].
-111\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China +114\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China have a public debate on genetically modified organisms? A discourse network analysis of public debate on Weibo. *Public Understanding of Science*, *31*(6), 732–750. @@ -1048,7 +1073,7 @@ Science*, *31*(6), 732–750.
-112\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The +115\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The injustice of procedure, distribution, and recognition within the discourse of air pollution in Delhi, India. *Environmental Sociology*, *9*(2), 176–189. @@ -1057,7 +1082,7 @@ discourse of air pollution in Delhi, India. *Environmental Sociology*,
-113\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: +116\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: Exploring risk and stigma of COVID-19 in the context of beliefs, actors and hierarchies in India. *Current Sociology*, *69*(4), 492–511. @@ -1066,7 +1091,7 @@ and hierarchies in India. *Current Sociology*, *69*(4), 492–511.
-114\. Kammerer, M. (2017). *Climate politics at the intersection between +117\. Kammerer, M. (2017). *Climate politics at the intersection between international dynamics and national decision-making: A policy network approach* \[Doctoral Thesis, University of Zurich, Faculty of Arts; Social Sciences\]. @@ -1075,7 +1100,7 @@ Social Sciences\].
-115\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die +118\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die EU: Eine Diskursperspektive auf die deutsche und schweizerische Klimapolitik. In R. Careja, P. Emmenegger, & N. Giger (Eds.), *The european social model under pressure. Liber amicorum in honour of klaus @@ -1086,7 +1111,7 @@ armingeon* (pp. 599–623). Springer VS.
-116\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate +119\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate change policy: The maturation of a policy discourse in the national and international context. *Social Networks*, *75*, 65–77. @@ -1095,7 +1120,7 @@ international context. *Social Networks*, *75*, 65–77.
-117\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat +120\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat Jakarta-Bandung dalam media online. *Jurnal Ilmu Komunikasi UHO: Jurnal Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34. @@ -1104,7 +1129,7 @@ Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34.
-118\. Keller, S. (2023). *Analysis of the media discourse about meat and +121\. Keller, S. (2023). *Analysis of the media discourse about meat and meat substitutes in U.S. Media between 2016 and 2021* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -1114,7 +1139,7 @@ Change Research\].
-119\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: +122\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: Policy network analysis. In H. Van den Bulck, M. Puppis, K. Donders, & L. Van Audenhove (Eds.), *The Palgrave handbook of methods for media policy research* (pp. 471–491). Palgrave Macmillan. @@ -1124,7 +1149,7 @@ policy research* (pp. 471–491). Palgrave Macmillan.
-120\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme +123\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme digital dalam perdebatan isu “presiden tiga periode” di Twitter. *Jurnal Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94. @@ -1133,7 +1158,7 @@ Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94.
-121\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? +124\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? Distributed-Ledger-Technologien im politischen Diskurs. Leitbilder, Ideen und Diskursnetzwerke im deutschen Bundestag der 19. Legislaturperiode* \[Master's Thesis, FU Berlin, Otto-Suhr-Institut fĂŒr @@ -1143,7 +1168,7 @@ Politikwissenschaft\].
-122\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech +125\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech Republic: Individual responsibility or structural issue? *Energy Research & Social Science*, *72*, 101877. @@ -1152,7 +1177,7 @@ Research & Social Science*, *72*, 101877.
-123\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling +126\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling environmental governance in the Lake Tahoe basin: A multiplex network approach. In M. Fischer & K. Ingold (Eds.), *Networks in water governance* (pp. 173–202). Palgrave Macmillan. @@ -1162,7 +1187,7 @@ governance* (pp. 173–202). Palgrave Macmillan.
-124\. Kooistra, M. N. (2022). *Space security and orbital +127\. Kooistra, M. N. (2022). *Space security and orbital sustainability. An institutional logics approach* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -1171,7 +1196,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-125\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). +128\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). Animals and climate change: A visual and discourse network analysis of Instagram posts. *Environmental Sociology*, *9*(4), 409–426. @@ -1180,7 +1205,7 @@ Instagram posts. *Environmental Sociology*, *9*(4), 409–426.
-126\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors +129\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors and justifications in media debates on Arctic climate change in Finland and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117. @@ -1189,7 +1214,7 @@ and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117.
-127\. Kukkonen, A. (2018). *Discourse networks and justifications of +130\. Kukkonen, A. (2018). *Discourse networks and justifications of climate change policy: News media debates in Canada, the United States, Finland, France, Brazil and India* \[Doctoral Dissertation, University of Helsinki, Faculty of Social Sciences\]. @@ -1199,17 +1224,17 @@ of Helsinki, Faculty of Social Sciences\].
-128\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to +131\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to politicization of science: How the forestry coalition challenged the scientific consensus in the Finnish news media debate on increased -logging. *Society & Natural Resources*. +logging. *Society & Natural Resources*, *37*(1), 91–112.
-129\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy +132\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy interface as a discourse network: Finland’s climate change policy 2002–2015. *Politics and Governance*, *8*(2), 200. @@ -1218,7 +1243,7 @@ interface as a discourse network: Finland’s climate change policy
-130\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy +133\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy coalitions, beliefs and climate change policy in the United States. *Public Administration*, *95*(3), 713–729. @@ -1227,7 +1252,7 @@ coalitions, beliefs and climate change policy in the United States.
-131\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., +134\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., Lahsen, M., & Stoddart, M. C. J. (2018). International organizations, advocacy coalitions, and domestication of global norms: Debates on climate change in Canada, the US, Brazil, and India. *Environmental @@ -1238,7 +1263,7 @@ Science & Policy*, *81*, 54–62.
-132\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., +135\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., Kuhn, J., & PadĂł, S. (2020). DEbateNet-mig15: Tracing the 2015 immigration debate in Germany over time. *Proceedings of the Twelfth Language Resources and Evaluation Conference*, 919–927. @@ -1248,7 +1273,7 @@ Language Resources and Evaluation Conference*, 919–927.
-133\. Laurer, M., & Seidl, T. (2021). Regulating the European +136\. Laurer, M., & Seidl, T. (2021). Regulating the European data-driven economy: A case study on the general data protection regulation. *Policy & Internet*, *13*(2), 257–277. @@ -1257,7 +1282,7 @@ regulation. *Policy & Internet*, *13*(2), 257–277.
-134\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem +137\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem Discourse Network Analyzer (DNA). In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. 391–404). Springer VS. @@ -1267,7 +1292,7 @@ Visualisierungen* (pp. 391–404). Springer VS.
-135\. Leifeld, P. (2013). Reconceptualizing major policy change in the +138\. Leifeld, P. (2013). Reconceptualizing major policy change in the advocacy coalition framework: A discourse network analysis of German pension politics. *Policy Studies Journal*, *41*(1), 169–198. @@ -1276,7 +1301,7 @@ pension politics. *Policy Studies Journal*, *41*(1), 169–198.
-136\. Leifeld, P. (2014). Polarization of coalitions in an agent-based +139\. Leifeld, P. (2014). Polarization of coalitions in an agent-based model of political discourse. *Computational Social Networks*, *1*(1), 1–22. @@ -1284,7 +1309,7 @@ model of political discourse. *Computational Social Networks*, *1*(1),
-137\. Leifeld, P. (2016). *Policy debates as dynamic networks: German +140\. Leifeld, P. (2016). *Policy debates as dynamic networks: German pension politics and privatization discourse*. Campus. @@ -1292,7 +1317,7 @@ pension politics and privatization discourse*. Campus.
-138\. Leifeld, P. (2017). Discourse network analysis: Policy debates as +141\. Leifeld, P. (2017). Discourse network analysis: Policy debates as dynamic networks. In J. N. Victor, A. H. Montgomery, & M. N. Lubell (Eds.), *The Oxford handbook of political networks* (pp. 301–325). Oxford University Press. @@ -1302,7 +1327,7 @@ Oxford University Press.
-139\. Leifeld, P. (2020). Policy debates and discourse network analysis: +142\. Leifeld, P. (2020). Policy debates and discourse network analysis: A research agenda. *Politics and Governance*, *8*(2), 180–183. @@ -1310,7 +1335,7 @@ A research agenda. *Politics and Governance*, *8*(2), 180–183.
-140\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition +143\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition formation in policy debates*. arXiv Preprint. @@ -1318,7 +1343,7 @@ formation in policy debates*. arXiv Preprint.
-141\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and +144\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and the conflict over software patents in Europe. *European Journal of Political Research*, *51*(3), 382–409. @@ -1327,7 +1352,7 @@ Political Research*, *51*(3), 382–409.
-142\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. +145\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. (2021). Belief system alignment and cross-sectoral advocacy efforts in policy debates. *Journal of European Public Policy*, 1–24. @@ -1336,7 +1361,7 @@ policy debates. *Journal of European Public Policy*, 1–24.
-143\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss +146\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss von Diskurskoalitionen auf Vermögensbesteuerung in Deutschland, 1995–2015: Eine Diskursnetzwerkanalyse von Policy-Wandel in der Steuerpolitik* \[Master's Thesis, FernUniversitĂ€t Hagen, FakultĂ€t fĂŒr @@ -1347,15 +1372,16 @@ Kultur- und Sozialwissenschaften, Institut fĂŒr Politikwissenschaft\].
-144\. Lemke, N., Trein, P., & Varone, F. (2023). *Agenda-setting in +147\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in nascent policy subsystems: Issue and instrument priorities across -venues*. *56*, 633–655. +venues. *Policy Sciences*, *56*, 633–655. +
-145\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., +148\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., Lagabrielle, E., Seen, D. L., & Degenne, P. (2017). Collaborative landscape research in Reunion Island: Using spatial modelling and simulation to support territorial foresight and urban planning. *Applied @@ -1365,7 +1391,7 @@ Geography*, *78*, 66–77.
-146\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s +149\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s family planning: Perspectives of advocacy coalitions. *International Journal of Environmental Research and Public Health*, *20*(6), 5204. @@ -1374,7 +1400,7 @@ Journal of Environmental Research and Public Health*, *20*(6), 5204.
-147\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, +150\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, M. (2023). Slovak MPs’ response to the 2022 Russian invasion of Ukraine in light of conspiracy theories and the polarization of political discourse. *Humanities and Social Sciences Communications*, *10*(1), @@ -1384,7 +1410,7 @@ discourse. *Humanities and Social Sciences Communications*, *10*(1),
-148\. Ličková, V. (2023). *Coal framing in the Indian political +151\. Ličková, V. (2023). *Coal framing in the Indian political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1392,7 +1418,7 @@ Studies\].
-149\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in +152\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in the acceleration phase of the European energy transition. *Environmental Innovation and Societal Transitions*, *40*, 262–282. @@ -1401,7 +1427,7 @@ Innovation and Societal Transitions*, *40*, 262–282.
-150\. Lockhart, C. (2014). *Discourse network analysis of the Northern +153\. Lockhart, C. (2014). *Discourse network analysis of the Northern Gateway Pipeline project: Assessing environmental governance in the joint review panel process* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental @@ -1411,7 +1437,7 @@ Governance Section\].
-151\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & +154\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & Wagner, P. M. (2021). Unity in diversity? When advocacy coalitions and policy beliefs grow trees in South Africa. *Land Use Policy*, *102*, 105283. @@ -1420,7 +1446,7 @@ policy beliefs grow trees in South Africa. *Land Use Policy*, *102*,
-152\. MalkamÀki, A. (2019). *On the human impacts and governance of +155\. MalkamÀki, A. (2019). *On the human impacts and governance of large-scale tree plantations* \[Doctoral Dissertation, University of Helsinki, Faculty of Agriculture; Forestry\]. @@ -1429,7 +1455,7 @@ Helsinki, Faculty of Agriculture; Forestry\].
-153\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & +156\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & YlÀ-Anttila, T. (2023). *Complex coalitions: Political alliances across relational contexts*. arXiv:2308.14422. @@ -1438,7 +1464,7 @@ relational contexts*. arXiv:2308.14422.
-154\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & +157\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & YlĂ€-Anttila, T. (2021). On the acoustics of policy learning: Can co-participation in policy forums break up echo chambers? *Policy Studies Journal*, *49*(2), 431–456. @@ -1447,7 +1473,7 @@ Studies Journal*, *49*(2), 431–456.
-155\. Mardiah, A. N. R. (2018). *Interface between disaster and +158\. Mardiah, A. N. R. (2018). *Interface between disaster and development: Local economic revival through collaborative post-disaster recovery governance and network in Indonesia* \[PhD thesis, University of Leeds, School of Geography\]. @@ -1457,7 +1483,7 @@ of Leeds, School of Geography\].
-156\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward +159\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward integrated and inclusive disaster risk reduction in Indonesia: Review of regulatory frameworks and institutional networks. In R. Djalante, M. Garschagen, F. Thomalla, & R. Shaw (Eds.), *Disaster risk reduction in @@ -1468,7 +1494,7 @@ Indonesia* (pp. 57–84). Springer.
-157\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. +160\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. A., & Hodgson, D. J. (2023). Stakeholder discourse coalitions and polarisation in the hen harrier conservation debate in news media. *People and Nature*, *5*(2), 668–683. @@ -1478,7 +1504,7 @@ polarisation in the hen harrier conservation debate in news media.
-158\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the +161\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the networking structuration processes of the urban resilience concept in Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1), 46–57. @@ -1487,7 +1513,7 @@ Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1),
-159\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing +162\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing transitions through the lens of discourse networks: Coal phase-out in Germany. *Environmental Innovation and Societal Transitions*, *40*, 315–331. @@ -1496,7 +1522,7 @@ Germany. *Environmental Innovation and Societal Transitions*, *40*,
-160\. Mayer, C. D. (2022). *New west tension and threatened species +163\. Mayer, C. D. (2022). *New west tension and threatened species protection: The western Joshua tree conservation debate in the Morongo Basin, california* \[Master's Thesis, California State University, Long Beach, Department of Geography\]. @@ -1506,7 +1532,7 @@ Beach, Department of Geography\].
-161\. McDonald, E. (2019). *Energy security in the age of +164\. McDonald, E. (2019). *Energy security in the age of interconnection: Cyber-threat framing in British political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1515,7 +1541,7 @@ interconnection: Cyber-threat framing in British political discourse*
-162\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of +165\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of floating signifier: Discourses and network analysis in the bioeconomy policy processes in Argentina and Uruguay. *Forest Policy and Economics*, *154*, 103039. @@ -1525,7 +1551,7 @@ Economics*, *154*, 103039.
-163\. Miles, A. (2020). *Changes in social networks and narratives +166\. Miles, A. (2020). *Changes in social networks and narratives associated with Lake Erie water quality management after the 2014 Toledo water crisis* \[Master's Thesis, The Ohio State University, Graduate Program in Environment; Natural Resources\]. @@ -1535,7 +1561,7 @@ Program in Environment; Natural Resources\].
-164\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear +167\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear energy in the European Union and its implications* \[Doctoral Dissertation, Masaryk University, Department of International Relations; European Studies\]. @@ -1544,7 +1570,7 @@ European Studies\].
-165\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes +168\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes diffuse in space – explaining a missed transition in San Diego’s water sector. *Environmental Innovation and Societal Transitions*, *44*, 29–47. @@ -1553,7 +1579,7 @@ sector. *Environmental Innovation and Societal Transitions*, *44*,
-166\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, +169\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, renewable energy, or both? Public opinion on small hydropower plants in Serbia. *Post-Communist Economies*, *34*(5), 684–713. @@ -1562,7 +1588,7 @@ Serbia. *Post-Communist Economies*, *34*(5), 684–713.
-167\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). +170\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). Relational coupling of multiple streams: The case of COVID-19 infections in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374. @@ -1571,7 +1597,7 @@ in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374.
-168\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? +171\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? For whom? Intersectionality in Canada’s feminist international assistance policy. *International Journal*, *75*(3), 329–348. @@ -1580,7 +1606,7 @@ assistance policy. *International Journal*, *75*(3), 329–348.
-169\. Muller, A. (2014). Het meten van discourscoalities met +172\. Muller, A. (2014). Het meten van discourscoalities met discoursnetwerkanalyse: Naar een formele analyse van het politieke vertoog. *Res Publica*, *56*(3), 337–364. @@ -1589,7 +1615,7 @@ vertoog. *Res Publica*, *56*(3), 337–364.
-170\. Muller, A. (2015). Using discourse network analysis to measure +173\. Muller, A. (2015). Using discourse network analysis to measure discourse coalitions: Towards a formal analysis of political discourse. *World Political Science*, *11*(2), 377–404. @@ -1598,7 +1624,7 @@ discourse coalitions: Towards a formal analysis of political discourse.
-171\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big +174\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big tobacco? Discourse network analysis of the cigarette advertising debate and policy in Indonesia. *Journal of Communication Inquiry*. @@ -1607,7 +1633,7 @@ and policy in Indonesia. *Journal of Communication Inquiry*.
-172\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine +175\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS. @@ -1615,7 +1641,7 @@ Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS.
-173\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den +176\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 153–177). transcript Verlag. @@ -1625,7 +1651,7 @@ politische Konzepte* (pp. 153–177). transcript Verlag.
-174\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere +177\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 77–98). transcript @@ -1635,7 +1661,7 @@ Verlag.
-175\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level +178\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level governance dynamics from a discourse network perspective: The debate over air pollution regulation in Germany. *Environmental Sciences Europe*, *34*(62), 1–18. @@ -1644,7 +1670,7 @@ Europe*, *34*(62), 1–18.
-176\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A +179\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A discourse network analysis of the evolution of the conflict over Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700. @@ -1653,15 +1679,16 @@ Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700.
-177\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate +180\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate action: Comparing the evolution of narratives using the “narrative rate” -index. *Review of Policy Research*. +index. *Review of Policy Research*, *40*(6), 1093–1119. +
-178\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die +181\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS. @@ -1669,7 +1696,7 @@ Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS.
-179\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in +182\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in South Korea’s nuclear power policy making: An advocacy coalition framework approach to policy knowledge. *Politics & Policy*, *51*(2), 201–221. @@ -1678,7 +1705,7 @@ framework approach to policy knowledge. *Politics & Policy*, *51*(2),
-180\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames +183\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames of advocacy coalitions in South Korea’s nuclear energy policy. *Review of Policy Research*, *39*(4), 387–410. @@ -1687,7 +1714,7 @@ of Policy Research*, *39*(4), 387–410.
-181\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position +184\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position und Einfluss der Kirchen in der deutschen Debatte um die embryonale Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.), *Religionspolitik und Politik der Religionen in Deutschland* (pp. @@ -1697,7 +1724,7 @@ Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.),
-182\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting +185\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting in emerging economies – a systematic literature review and research agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820. @@ -1706,7 +1733,7 @@ agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820.
-183\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ +186\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ opozice vƯči hlubinnĂ©mu ĂșloĆŸiĆĄti radioaktivnĂ­ch odpadĆŻ v ČeskĂ© republice* \[PhD thesis, Masarykova univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\]. @@ -1715,7 +1742,7 @@ univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\].
-184\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech +187\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech newspaper coverage: A one man show. In D. Tindall, M. C. J. Stoddart, & R. E. Dunlap (Eds.), *Handbook of anti-environmentalism* (pp. 84–106). Edward Elgar Publishing. @@ -1724,7 +1751,7 @@ Edward Elgar Publishing.
-185\. Ohlendorf, N. (2022). *The political economy of energy +188\. Ohlendorf, N. (2022). *The political economy of energy transitions* \[Doctoral Thesis, Technical University of Berlin, FakultĂ€t VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\]. @@ -1733,7 +1760,7 @@ VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\].
-186\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in +189\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in multi-sector transitions – discourse analysis on hydrogen in Germany. *Environmental Innovation and Societal Transitions*, *47*, 100692. @@ -1742,7 +1769,7 @@ multi-sector transitions – discourse analysis on hydrogen in Germany.
-187\. Ohno, T. (2022). Advocacy coalition framework in environmental +190\. Ohno, T. (2022). Advocacy coalition framework in environmental governance studies: Explaining major policy change for a large dam removal in Japan. *International Review of Public Policy*, *4*(1). @@ -1751,7 +1778,7 @@ removal in Japan. *International Review of Public Policy*, *4*(1).
-188\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa +191\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa na polĂ­tica de transporte marĂ­timo de cargas no Brasil: Privatização, descentralização e abertura para o capital estrangeiro. *Caderno CRH – Revista de CiĂȘncias Sociais Do Centro de Estudos Pesquisas e Humanidades @@ -1762,7 +1789,7 @@ Da Universidade Federal Da Bahia*, *36*(2), 1–21.
-189\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of +192\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of oil and gas governance in Ghana* \[PhD thesis, University of Colorado Denver, School of Public Affairs\]. @@ -1771,7 +1798,7 @@ Denver, School of Public Affairs\].
-190\. Osei-Kojo, A. (2023). Analysing the stability of advocacy +193\. Osei-Kojo, A. (2023). Analysing the stability of advocacy coalitions and policy frames in Ghana’s oil and gas governance. *Policy & Politics*, *51*(1), 71--90. @@ -1780,7 +1807,7 @@ coalitions and policy frames in Ghana’s oil and gas governance. *Policy
-191\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, +194\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, B. (2018). Natural gas market integration in the Visegrad 4 region: An example to follow or to avoid? *Energy Policy*, *112*, 184–197. @@ -1789,7 +1816,7 @@ example to follow or to avoid? *Energy Policy*, *112*, 184–197.
-192\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & +195\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & Kuhn, J. (2019). Who sides with whom? Towards computational construction of discourse networks for political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics*, @@ -1799,7 +1826,7 @@ Annual Meeting of the Association for Computational Linguistics*,
-193\. Palladino, N. (2021). The role of epistemic communities in the +196\. Palladino, N. (2021). The role of epistemic communities in the “constitutionalization” of internet governance: The example of the European Commission high-level expert group on artificial intelligence. *Telecommunications Policy*, *45*(6), 102149. @@ -1809,7 +1836,7 @@ European Commission high-level expert group on artificial intelligence.
-194\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, +197\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, P. (2020). Interactions among sustainable development goals: Knowledge for identifying multipliers and virtuous cycles. *Sustainable Development*, *28*(5), 1236–1250. @@ -1818,7 +1845,7 @@ Development*, *28*(5), 1236–1250.
-195\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des +198\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des Ă©chelles de la sĂ©curitĂ© dans une rĂ©gion Ă  gĂ©omĂ©trie variable* \[PhD thesis, UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\]. @@ -1828,7 +1855,7 @@ UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\].
-196\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and +199\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and climate change. A complex reflexive systems approach to energy transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.), *The routledge handbook of ideology and international relations* (pp. @@ -1838,7 +1865,7 @@ transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.),
-197\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing +200\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing psychedelics: The segregation of spiritual and scientific narratives within the r/microdosing online community. *Journal of Psychedelic Studies*, *7*(2), 119–128. @@ -1847,7 +1874,7 @@ Studies*, *7*(2), 119–128.
-198\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse +201\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse network analysis of French hydrogen politics* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1855,7 +1882,7 @@ University, Faculty of Social Studies\].
-199\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking +202\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking analysis as alternative research method in communication science studies – discourse networking analysis sebagai metode penelitian alternatif dalam kajian ilmu komunikasi. *Jurnal Penelitian Komunikasi Dan Opini @@ -1865,7 +1892,7 @@ Publik*, *21*(2), 126–136.
-200\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). +203\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). Discourse network analysis pada stakeholder dan integrated value creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*, *7*(2), 256–274. @@ -1874,7 +1901,7 @@ creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*,
-201\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., +204\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., Espressivo, A., Herlinda, O., Meilissa, Y., & Saminarsih, D. S. (2023). The advocacy coalition of sugar-sweetened beverage taxes in Indonesia. *BMJ Global Health*, *8*(Suppl 8), 1–13. @@ -1884,7 +1911,7 @@ The advocacy coalition of sugar-sweetened beverage taxes in Indonesia.
-202\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder +205\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder environmental governance in action: REDD+ discourse coalitions in Tanzania. *Ecology and Society*, *19*(2), 66–76. @@ -1893,7 +1920,7 @@ Tanzania. *Ecology and Society*, *19*(2), 66–76.
-203\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education +206\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education policy discourse: Philanthropic funders as entrepreneurs in policy networks. *Interest Groups & Advocacy*, *7*(3), 258–288. @@ -1902,7 +1929,7 @@ networks. *Interest Groups & Advocacy*, *7*(3), 258–288.
-204\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the +207\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the political economy of knowledge production shapes education policy: The case of teacher evaluation in federal policy discourse. *Educational Evaluation and Policy Analysis*, *43*(3), 472–494. @@ -1912,7 +1939,7 @@ Evaluation and Policy Analysis*, *43*(3), 472–494.
-205\. Rennkamp, B. (2019). Power, coalitions and institutional change in +208\. Rennkamp, B. (2019). Power, coalitions and institutional change in South African climate policy. *Climate Policy*, *19*(6), 756–770. @@ -1920,7 +1947,7 @@ South African climate policy. *Climate Policy*, *19*(6), 756–770.
-206\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. +209\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. (2017). Competing coalitions: The politics of renewable energy and fossil fuels in Mexico, South Africa and Thailand. *Energy Research & Social Science*, *34*, 214–223. @@ -1930,7 +1957,7 @@ Social Science*, *34*, 214–223.
-207\. Rinscheid, A. (2018). *Behavioral and institutionalist +210\. Rinscheid, A. (2018). *Behavioral and institutionalist perspectives on preference formation in a contested political context: The case of divesting from nuclear power* \[Doctoral Dissertation, University of St. Gallen, School of Management, Economics, Law, Social @@ -1941,7 +1968,7 @@ Sciences; International Affairs\].
-208\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy +211\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy change: Exploring the role of subsystem polarization in nuclear energy policymaking. *European Policy Analysis*, *1*(2), 34–70. @@ -1950,7 +1977,7 @@ policymaking. *European Policy Analysis*, *1*(2), 34–70.
-209\. Rinscheid, A. (2020). Business power in noisy politics: An +212\. Rinscheid, A. (2020). Business power in noisy politics: An exploration based on discourse network analysis and survey data. *Politics and Governance*, *8*(2), 286–297. @@ -1959,7 +1986,7 @@ exploration based on discourse network analysis and survey data.
-210\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. +213\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. (2020). Why do junctures become critical? Political discourse, agency, and joint belief shifts in comparative perspective. *Regulation & Governance*, *14*(4), 653–673. @@ -1968,7 +1995,7 @@ Governance*, *14*(4), 653–673.
-211\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The +214\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The diffusion of protest against the anti-counterfeiting trade agreement in the age of austerity* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -1978,7 +2005,7 @@ Department of Political; Social Sciences\].
-212\. Rone, J. (2023). Instrumentalising sovereignty claims in British +215\. Rone, J. (2023). Instrumentalising sovereignty claims in British pro- and anti-Brexit mobilisations. *The British Journal of Politics and International Relations*, *25*(3), 444–461. @@ -1987,7 +2014,7 @@ International Relations*, *25*(3), 444–461.
-213\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff +216\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff increase policy in discourse analysis network. *Jurnal Komunikasi*, *17*(1), 62–75. @@ -1995,7 +2022,7 @@ increase policy in discourse analysis network. *Jurnal Komunikasi*,
-214\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, +217\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, right now: Storylines and social identities in coalition building in a local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247. @@ -2004,7 +2031,7 @@ local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247.
-215\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, +218\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, R. R. (2023). Bias media mengenai pemberitaan batalnya Indonesia menjadi tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co. *Jurnal Ilmiah Wahana Pendidikan*, *9*(23), 547–556. @@ -2014,7 +2041,7 @@ tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co.
-216\. Schaub, S. (2021). *The politics of water protection* \[Doctoral +219\. Schaub, S. (2021). *The politics of water protection* \[Doctoral Dissertation, University of Heidelberg, Faculty of Economics; Social Studies, Institute of Political Science\]. @@ -2023,7 +2050,7 @@ Studies, Institute of Political Science\].
-217\. Schaub, S. (2021). Public contestation over agricultural +220\. Schaub, S. (2021). Public contestation over agricultural pollution: A discourse network analysis on narrative strategies in the policy process. *Policy Sciences*, *54*(4), 783–821. @@ -2032,7 +2059,7 @@ policy process. *Policy Sciences*, *54*(4), 783–821.
-218\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable +221\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable pharmacy? The influence of public debates on policy responses to pharmaceutical contaminants in water. *Environmental Sciences Europe*, *32*(1), 1–19. @@ -2041,7 +2068,7 @@ pharmaceutical contaminants in water. *Environmental Sciences Europe*,
-219\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy +222\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy network approaches: Evidence from water policy on micropollutants. *Politics and Governance*, *8*(2), 184–199. @@ -2050,7 +2077,7 @@ network approaches: Evidence from water policy on micropollutants.
-220\. Schmid, N. (2020). *The politics of technological change – case +223\. Schmid, N. (2020). *The politics of technological change – case studies from the energy sector* \[Doctoral Thesis, ETH ZĂŒrich, Department of Humanities, Social; Political Sciences, Energy; Technology Policy Group\]. @@ -2059,7 +2086,7 @@ Policy Group\].
-221\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining +224\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining advocacy coalition change with policy feedback. *Policy Studies Journal*, *48*(4), 1109–1134. @@ -2067,7 +2094,7 @@ Journal*, *48*(4), 1109–1134.
-222\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A +225\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A discourse network analysis of German parliamentary debates* \[Diploma Thesis, Charles University, Department of Security Studies\]. @@ -2076,7 +2103,7 @@ Thesis, Charles University, Department of Security Studies\].
-223\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, +226\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, partisanship and paradigmatic change in energy policy – analyzing parliamentary discourse in Germany over 30 years. *Climate Policy*, *19*(6), 771–786. @@ -2085,7 +2112,7 @@ parliamentary discourse in Germany over 30 years. *Climate Policy*,
-224\. Schmitz, L. (2018). *From coherence to coheritization. Explaining +227\. Schmitz, L. (2018). *From coherence to coheritization. Explaining the rise of policy coherence in EU external policy* \[Master's Thesis, Radboud University Nijmegen, Faculteit der Managementwetenschappen\]. @@ -2094,7 +2121,7 @@ Radboud University Nijmegen, Faculteit der Managementwetenschappen\].
-225\. Schmitz, L., & Eimer, T. R. (2020). From coherence to +228\. Schmitz, L., & Eimer, T. R. (2020). From coherence to coheritization: Explaining the rise of policy coherence in EU external policy. *Globalizations*, *17*(4), 629–647. @@ -2103,7 +2130,7 @@ policy. *Globalizations*, *17*(4), 629–647.
-226\. Schmitz, L., & Seidl, T. (2022). As open as possible, as +229\. Schmitz, L., & Seidl, T. (2022). As open as possible, as autonomous as necessary: Understanding the rise of open strategic autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*, *61*(3), 834–852. @@ -2112,7 +2139,7 @@ autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*,
-227\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of +230\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of technology legitimacy: Contending storylines surrounding wind energy in Austria and Switzerland. *Technological Forecasting and Social Change*, *198*, 122929. @@ -2121,7 +2148,7 @@ Austria and Switzerland. *Technological Forecasting and Social Change*,
-228\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and +231\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and displacements in policy discourse: The climate change issue in Germany 2007–2010. In S. Silvern & S. Young (Eds.), *Environmental change and sustainability* (pp. 157–184). Intech. @@ -2130,7 +2157,7 @@ sustainability* (pp. 157–184). Intech.
-229\. Schulz, C. (2020). Forest conservation through markets? A +232\. Schulz, C. (2020). Forest conservation through markets? A discourse network analysis of the debate on funding mechanisms for REDD+ in Brazil. *Environmental Communication*, *14*(2), 202–218. @@ -2139,7 +2166,7 @@ in Brazil. *Environmental Communication*, *14*(2), 202–218.
-230\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of +233\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of Twitter and newspapers: Lessons learned from the nuclear debate in the 2022 French presidential campaign. *French Politics*, *21*(2), 195–221. @@ -2148,7 +2175,7 @@ Twitter and newspapers: Lessons learned from the nuclear debate in the
-231\. Seidl, T. (2021). *Ideas, politics, and technological change: +234\. Seidl, T. (2021). *Ideas, politics, and technological change: Essays on the comparative political economy of digital capitalism* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2157,7 +2184,7 @@ Sciences\].
-232\. Seidl, T. (2022). The politics of platform capitalism: A case +235\. Seidl, T. (2022). The politics of platform capitalism: A case study on the regulation of Uber in New York. *Regulation & Governance*, *16*(2), 357–374. @@ -2165,7 +2192,7 @@ study on the regulation of Uber in New York. *Regulation & Governance*,
-233\. Selle, L. (2017). What multi-level parliamentary system? +236\. Selle, L. (2017). What multi-level parliamentary system? Parliamentary discourses in EU budgetary negotiations (MFF 2014–2020). In S. Becker, M. W. Bauer, & A. De Feo (Eds.), *The new politics of the European Union budget* (pp. 149–172). Nomos. @@ -2175,7 +2202,7 @@ European Union budget* (pp. 149–172). Nomos.
-234\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z +237\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2183,7 +2210,7 @@ University, Faculty of Social Studies\].
-235\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris +238\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris India: Unpacking consensus and conflict through storylines and discourse coalitions. *Energy Research & Social Science*, *91*, 102687. @@ -2192,16 +2219,16 @@ coalitions. *Energy Research & Social Science*, *91*, 102687.
-236\. Shukla, R., & Swarnakar, P. (2022). Energy transition and +239\. Shukla, R., & Swarnakar, P. (2022). Energy transition and dialectics: Tracing discursive resistance to coal through discourse -coalition in India. *Globalizations*. +coalition in India. *Globalizations*, *20*(8), 1296–1311.
-237\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi +240\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi virus corona dengan discourse network analysis. *Jurnal Kebijakan Kesehatan Indonesia: JKKI*, *9*(2), 98–106. @@ -2210,16 +2237,16 @@ Kesehatan Indonesia: JKKI*, *9*(2), 98–106.
-238\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying +241\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying influence on EU efforts to regulate CO2 emissions of cars using network -analysis. *Interest Groups & Advocacy*. +analysis. *Interest Groups & Advocacy*, *12*, 388–412.
-239\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan +242\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 34–49. @@ -2227,7 +2254,7 @@ UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2),
-240\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada +243\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina. *Kommunikatif*, *12*(1), 37–50. @@ -2235,7 +2262,7 @@ kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina.
-241\. Sohn, C. (2023). The impact of rebordering on cross-border +244\. Sohn, C. (2023). The impact of rebordering on cross-border cooperation actors’ discourses in the Öresund region. A semantic network approach. *Geografiska Annaler: Series B, Human Geography*. @@ -2244,7 +2271,7 @@ approach. *Geografiska Annaler: Series B, Human Geography*.
-242\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu +245\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*, *7*(2), 130–145. @@ -2252,7 +2279,7 @@ ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*,
-243\. Stancioff, C. E. (2016). Locality and landscape change: Cultural +246\. Stancioff, C. E. (2016). Locality and landscape change: Cultural values and social-ecological resiliency in the Kalinago territory. In T. Collins, G. Kindermann, C. Newman, & N. Cronin (Eds.), *Landscape values: Place and praxis. Conference, galway, 29th june–2nd july, 2016* @@ -2263,7 +2290,7 @@ values: Place and praxis. Conference, galway, 29th june–2nd july, 2016*
-244\. Starke, J. (2016). *Generating policy change in situations of +247\. Starke, J. (2016). *Generating policy change in situations of equilibrium: Shifting discourse networks in the case of wild circus animals in Germany* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance @@ -2273,7 +2300,7 @@ Section\].
-245\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. +248\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. J., & Termeer, K. J. A. M. (2023). “Green future” versus “planetary boundaries”? Evolving online discourse coalitions in European bioeconomy conflicts. *Journal of Cleaner Production*, *425*, 139058. @@ -2283,7 +2310,7 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
-246\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +249\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2291,7 +2318,7 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-247\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +250\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\]. @@ -2300,7 +2327,7 @@ of Governance, Master of Public Policy\].
-248\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +251\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2309,7 +2336,7 @@ co-exist?* Palgrave Macmillan.
-249\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +252\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2319,7 +2346,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-250\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +253\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2329,7 +2356,7 @@ alignments through discourse networks and intersectoral relationships.
-251\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +254\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2338,7 +2365,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-252\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +255\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2347,7 +2374,7 @@ Sociologie*, *53*(3), 316–336.
-253\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +256\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2356,7 +2383,7 @@ and the cultural dynamics of multilevel climate governance.
-254\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +257\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2366,16 +2393,16 @@ climate change: Canadian national news and mediated policy networks.
-255\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +258\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy -Research*. +Research*, *40*(6), 1144–1168.
-256\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +259\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2384,7 +2411,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-257\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +260\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2393,7 +2420,7 @@ Economics and Environment*, *83*(4), 62–75.
-258\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +261\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2402,7 +2429,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-259\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +262\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2412,7 +2439,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-260\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +263\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2420,7 +2447,7 @@ thesis, Utrecht University\].
-261\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +264\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2429,7 +2456,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-262\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +265\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2438,7 +2465,7 @@ International Conference on Business and Public Administration (AICoBPA
-263\. Taranger, K. K. (2020). *The institutionalisation of climate +266\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2447,7 +2474,7 @@ Environmental Governance Section\].
-264\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +267\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2456,7 +2483,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-265\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +268\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2466,7 +2493,7 @@ Glasgow, School of Social; Political Sciences\].
-266\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +269\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates on hydraulic fracturing. Comparing coalition politics in north america @@ -2477,7 +2504,7 @@ and europe* (pp. 177–200). Palgrave Macmillan.
-267\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +270\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2486,7 +2513,7 @@ of Policy Research*, *34*(3), 310–330.
-268\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +271\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2495,7 +2522,7 @@ tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu*
-269\. Tuinenburg, J. (2019). *The effect of discourse networks on the +272\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2504,7 +2531,7 @@ Governance\].
-270\. Umansky Casapa, N. (2022). *Securitization and social media +273\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2513,7 +2540,7 @@ Dublin, School of Politics; International Relations\].
-271\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +274\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2521,7 +2548,7 @@ Social Studies\].
-272\. Vaughan, M. (2020). Talking about tax: The discursive distance +275\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2530,7 +2557,7 @@ Politics*, *17*(2), 114–129.
-273\. Vedres, B. (2022). Multivocality and robust action dynamics in +276\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2538,7 +2565,7 @@ political discourse. *Poetics*, *90*, 101576.
-274\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +277\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2547,7 +2574,7 @@ Environmental Change*, *63*, 102117.
-275\. Vogeler, C. S. (2022). The integration of environmental objectives +278\. Vogeler, C. S. (2022). The integration of environmental objectives in the common agricultural policy—partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2556,7 +2583,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-276\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +279\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2565,16 +2592,25 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-277\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +280\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343.
+
+ +281\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of +the far right: How far-right actors become mainstream in public debates. +*Political Communication*, 1–20. + + +
+
-278\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +282\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2583,9 +2619,18 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
+
+ +283\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of +failure on socio-technical system configurations* \[Master’s thesis, +Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. + + +
+
-279\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +284\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2594,7 +2639,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-280\. Wallaschek, S. (2019). The discursive appeal to solidarity and +285\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2602,7 +2647,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-281\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +286\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2612,7 +2657,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-282\. Wallaschek, S. (2020). Analyzing the European parliamentary +287\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2622,7 +2667,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-283\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +288\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2631,7 +2676,7 @@ European Public Policy*, *27*(7), 1034–1053.
-284\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +289\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2640,7 +2685,7 @@ Economy*, *25*(2), 231–247.
-285\. Wallaschek, S. (2020). The discursive construction of solidarity: +290\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2648,7 +2693,7 @@ Studies*, *68*(1), 74–92.
-286\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +291\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2656,7 +2701,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-287\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +292\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2666,7 +2711,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-288\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +293\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2675,7 +2720,7 @@ the public sphere: A discourse network analysis of German newspapers
-289\. Wang, S. (2018). *Dynamic constructed climate change discourses +294\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2685,7 +2730,7 @@ Politics\].
-290\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +295\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2694,7 +2739,7 @@ Public Policy*, *44*(3), 354–368.
-291\. Wang, Y. (2021). Examining the actor coalitions and discourse +296\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2703,7 +2748,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-292\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +297\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2712,7 +2757,7 @@ Policy Analysis Archives*, *25*(34), 1–27.
-293\. Wang, Y. (2020). Understanding Congressional coalitions: A +298\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30. @@ -2721,7 +2766,7 @@ Student Succeeds act. *Education Policy Analysis Archives*, *28*(119),
-294\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +299\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & Hekkert, M. P. (2023). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. *Environmental Policy and Governance*. @@ -2731,7 +2776,7 @@ interest organizations in fragmented socio-technical systems.
-295\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +300\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2740,7 +2785,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-296\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +301\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2750,7 +2795,7 @@ The influence of global and national institutional dynamics.
-297\. Wu, J., & Liu, Y. (2020). Deception detection methods +302\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2759,7 +2804,7 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-298\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +303\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948. @@ -2768,7 +2813,7 @@ Information Technology*, *34*(9), 936–948.
-299\. Yan, K., Wu, H., Bu, K., & Wu, L. (2023). The college admission +304\. Yan, K., Wu, H., Bu, K., & Wu, L. (2023). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. *Higher Education Policy*. @@ -2776,7 +2821,7 @@ policy evolution from 2003 to 2020 in China – a social network analysis.
-300\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +305\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -2785,7 +2830,7 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-301\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +306\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32. @@ -2794,7 +2839,7 @@ infrastructure. *Politics & Policy*, *50*(1), 8–32.
-302\. Yordy, J., DurnovĂĄ, A., & Weible, C. M. (2023). Exploring +307\. Yordy, J., DurnovĂĄ, A., & Weible, C. M. (2023). Exploring emotional discourses: The case of COVID-19 protests in the US media. *Administrative Theory & Praxis*. @@ -2803,7 +2848,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-303\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +308\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -2812,7 +2857,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-304\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +309\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -2821,7 +2866,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-305\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +310\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), 107--129. @@ -2830,7 +2875,7 @@ transmission line sitings. *Public Policy and Administration*, *38*(1),
-306\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +311\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. From e298c47dfa26cf8ca7e779375aea174ac3b60bb7 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Tue, 16 Jul 2024 15:56:10 +0200 Subject: [PATCH 13/43] Updated bibliography --- bibliography/bibliography.bib | 281 +++++++++-- build/bibliography.md | 844 ++++++++++++++++++++-------------- rDNA/rDNA/DESCRIPTION | 2 +- 3 files changed, 733 insertions(+), 394 deletions(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index f7219459..90380ecf 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -42,6 +42,17 @@ @incollection{almiron2021think url={https://www.torrossa.com/en/resources/an/5093569#} } +@article{amrihani2024debate, + title={Debate on Rice Import Policy in {I}ndonesia: Discourse Network Analysis in Online Media}, + author={Amrihani, Haresti Asysy and Eriyanto, Eriyanto and Zulkifli, Muhammad Yunus}, + journal={Jurnal Komunikasi Ikatan Sarjana Komunikasi Indonesia}, + volume={9}, + number={1}, + pages={118--131}, + year={2024}, + doi={10.25008/jkiski.v9i1.1051} +} + @article{anshori2023who, author={Anshori, Mahfud and Pawito and Kartono, Drajat Tri and Hastjarjo, Sri}, title={Who Says What? {T}he Role of the Actor's Political Position in Ideograph Construction}, @@ -86,15 +97,41 @@ @article{aryal2022navigating doi={10.1016/j.forpol.2022.102768} } +@incollection{astner2024treaty, + title={The {T}reaty on the {P}rohibition of {N}uclear {W}eapons}, + author={Astner, Katja and K{\"u}tt, Moritz}, + year={2024}, + booktitle={{G}ermany and Nuclear Weapons in the 21st Century. {A}tomic {Z}eitenwende?}, + editor={K{\"u}hn, Ulrich}, + publisher={Routledge}, + address={London and New York}, + pages={203--229}, + chapter={9}, + doi={10.4324/9781003341161-13} +} + @article{baekgaard2024technocentric, title={The Technocentric Consensus: A Discourse Network Analysis of the {E}uropean Circular Economy Debate}, author={B{\ae}kgaard, Aksel S. and Engberg, Christian and Hasselbalch, Jacob A.}, journal={Journal of Environmental Policy \& Planning}, year={2024}, - note={Forthcoming}, + volume={26}, + number={2}, + pages={173--187}, doi={10.1080/1523908X.2024.2317793} } +@article{bafadhal2024konflik, + title={Konflik dan wacana media dalam pembangunan gereja di {P}alembang: Analisis jaringan diskursus}, + author={Bafadhal, Oemar Madri and Saraswaty, Erlisa and Handoko, Adi Inggit}, + journal={Jurnal Studi Komunikasi}, + volume={8}, + number={1}, + pages={217--226}, + year={2024}, + doi={10.25139/jsk.v8i1.6257} +} + @mastersthesis{bandau2021emerging, title={Emerging Institutions in the Global Space Sector. {A}n Institutional Logics Approach}, author={Bandau, Sanja Lisa}, @@ -187,10 +224,19 @@ @article{bhattacharya2023restrictive publisher={SAGE Publications Sage UK: London, England}, volume={29}, number={3}, - pages={554–-569}, + pages={554--569}, doi={10.1177/13540688221086226} } +@mastersthesis{bisht2023media, + title={Media Discourse Analysis: Solar Power in {I}ndia. {T}he Case of {G}ujarat}, + author={Bisht, Bhavesh Singh}, + year={2023}, + type={Master's Thesis}, + school={Masaryk University, Faculty of Social Studies, Department of International Relations and European Studies}, + url={https://is.muni.cz/th/k0ueo/} +} + @mastersthesis{blazek2021does, title={Does the Climate Matter? {D}iscourse Network Analysis of Climate Delayism in the {C}zech {R}epublic}, author={Bla{\v{z}}ek, David}, @@ -337,7 +383,7 @@ @phdthesis{brutschin2013dynamics } @article{buckton2019discourse, - title={A Discourse Network Analysis of {UK} Newspaper Coverage of the ``Sugar Tax'' Debate before and after the Announcement of the Soft Drinks Industry Levy}, + title={A Discourse Network Analysis of {UK} Newspaper Coverage of the ``Sugar Tax'' Debate before and after the Announcement of the {S}oft {D}rinks {I}ndustry {L}evy}, author={Buckton, Christina H. and Fergie, Gillian and Leifeld, Philip and Hilton, Shona}, journal={BMC Public Health}, volume={19}, @@ -395,11 +441,12 @@ @phdthesis{ciordia2020less } @article{clasing2024free, - title={How Free Tuition Cecame a Policy in {C}hile: The Importance of Policy Actors and their Beliefs}, - author={Clasing-Manquian, Paula}, - journal={Higher Education}, - year={2024}, - note={Forthcoming} + title={How Free Tuition Cecame a Policy in {C}hile: The Importance of Policy Actors and their Beliefs}, + author={Clasing-Manquian, Paula}, + journal={Higher Education}, + year={2024}, + note={Forthcoming}, + doi={10.1007/s10734-024-01188-z} } @mastersthesis{coronado2015reforma, @@ -412,21 +459,22 @@ @mastersthesis{coronado2015reforma } @article{cross2023speaking, - author={Cross, James P. and Greene, Derek and Umansky, Natalia and Cal\`{o}, Silvia}, - title={Speaking in Unison? {E}xplaining the Role of Agenda-Setter Constellations in the {ECB} Policy Agenda Using a Network-Based Approach}, - journal={Journal of European Public Policy}, - year={2023}, - doi={10.1080/13501763.2023.2242891}, - note={Forthcoming} + author={Cross, James P. and Greene, Derek and Umansky, Natalia and Cal\`{o}, Silvia}, + title={Speaking in Unison? {E}xplaining the Role of Agenda-Setter Constellations in the {ECB} Policy Agenda Using a Network-Based Approach}, + journal={Journal of European Public Policy}, + year={2023}, + doi={10.1080/13501763.2023.2242891}, + note={Forthcoming} } @incollection{dalimunthe2022depicting, - title={Depicting Mangrove's Potential as Blue Carbon Champion in Indonesia}, + title={Depicting Mangrove's Potential as Blue Carbon Champion in {I}ndonesia}, author={Dalimunthe, Syarifah Aini and Putri, Intan Adhi Perdana and Prasojo, Ari Purwanto Sarwo}, - booktitle={Assessing, Mapping and Modelling of Mangrove Ecosystem Services in the Asia-Pacific Region}, + booktitle={Assessing, Mapping and Modelling of Mangrove Ecosystem Services in the {A}sia-{P}acific Region}, pages={167--181}, editor={Dasgupta, Rajarshi and Hashimoto, Shizuka and Saito, Osamu}, year={2022}, + chapter={9}, publisher={Springer}, address={Singapore}, doi={10.1007/978-981-19-2738-6_9} @@ -453,7 +501,7 @@ @article{diaz2018competing } @mastersthesis{Drozdzynski2022common, - title={The Common Agricultural Policy Post 2020: An Analysis of the Beliefs of Selected Key Stakeholders}, + title={The {C}ommon {A}gricultural {P}olicy Post 2020: An Analysis of the Beliefs of Selected Key Stakeholders}, author={Drozdzynski, Franziska Alexandra}, year={2022}, school={University of Twente, Faculty of Behavioural, Management and Social Sciences}, @@ -466,8 +514,10 @@ @article{durel2024border author={Durel, Laurie}, journal={Journal of International Economic Law}, year={2024}, - doi={10.1093/jiel/jgae007}, - note={Forthcoming} + volume={27}, + number={1}, + pages={18--40}, + doi={10.1093/jiel/jgae007} } @phdthesis{duygan2018actor, @@ -516,7 +566,7 @@ @incollection{eberlein2020building title={Building Bridges: How Discourse Network Analysis ({DNA}) can Help {CSR} Research to Investigate the ``New'' Political Role of Corporations}, author={Eberlein, Burkard and Rinscheid, Adrian}, year={2020}, - booktitle={Politische Komplexit{\"a}t, Governance von Innovationen und Policy-Netzwerke}, + booktitle={Politische {K}omplexit{\"a}t, {G}overnance von {I}nnovationen und {P}olicy-{N}etzwerke}, editor={Nagel, Melanie and Kenis, Patrick and Leifeld, Philip and Schmedes, Hans-J{\"o}rg}, pages={139--146}, publisher={Springer VS}, @@ -525,7 +575,7 @@ @incollection{eberlein2020building } @book{eder2015irakkrieg, - title={Der Irakkrieg 2003}, + title={Der {I}rakkrieg 2003}, author={Eder, Franz}, year={2015}, publisher={Innsbruck University Press}, @@ -536,7 +586,7 @@ @book{eder2015irakkrieg @incollection{eder2023discourse, title={Discourse Network Analysis}, author={Eder, Franz}, - booktitle={Routledge Handbook of Foreign Policy Analysis Methods}, + booktitle={Routledge {H}andbook of Foreign Policy Analysis Methods}, year={2023}, editor={Mello, Patrick A. and Ostermann, Falk}, publisher={Taylor \& Francis}, @@ -632,7 +682,7 @@ @inproceedings{filippini2018trends pages={79--88}, publisher={Coventry University}, address={Coventry}, - doi={https://pureportal.coventry.ac.uk/en/publications/re-imagining-sustainable-food-planning-building-resourcefulness-f} + url={https://pureportal.coventry.ac.uk/en/publications/re-imagining-sustainable-food-planning-building-resourcefulness-f} } @article{fisher2019polycentricity, @@ -660,14 +710,13 @@ @article{fisher2013mapping } @article{fisher2013does, - title={Where does Political Polarization Come from? {L}ocating Polarization within the {US} Climate Change Debate}, + title={Where does Political Polarization Come from? {L}ocating Polarization within the {U.S.} Climate Change Debate}, author={Fisher, Dana R. and Waggle, Joseph and Leifeld, Philip}, journal={American Behavioral Scientist}, volume={57}, number={1}, pages={70--92}, year={2013}, - publisher={Sage Publications Sage CA: Los Angeles, CA}, doi={10.1177/0002764212463360} } @@ -800,6 +849,15 @@ @article{gkiouzepas2017climate doi={10.1080/17524032.2015.1047888} } +@article{goritz2024education, + title={Education in International Climate Pledges -- Identifying Education Framings in Countries Nationally Determined Contributions (NDCs)}, + author={Goritz, Alexandra and Kolleck, Nina}, + journal={Environmental Education Research}, + year={2024}, + note={Forthcoming}, + doi={10.1080/13504622.2024.2340504} +} + @mastersthesis{grunwald2023roadblocks, title={Roadblocks of Polarization: {M}echanisms of Cultural Resistance to a Speed Limit on {G}erman Highways}, author={Gr{\"u}nwald, Lotte}, @@ -978,6 +1036,15 @@ @article{hayward2023restrictive doi={10.1177/17446295211062383} } +@phdthesis{heermann2024preferences, + title={Preferences and Coalitions in {E}uropean {U}nion Internet Policy}, + author={Heermann, Max}, + year={2024}, + school={University of Konstanz, Department of Politics and Public Administration}, + type={Doctoral Dissertation}, + url={http://nbn-resolving.de/urn:nbn:de:bsz:352-2-9c3l4n366eux3} +} + @phdthesis{heiberg2022geography, title={The Geography of Configurations that Work}, author={Heiberg, Jonas}, @@ -1283,7 +1350,7 @@ @incollection{kenis2019analyzing title={Analyzing Policy-Making {II}: Policy Network Analysis}, author={Kenis, Patrick and Schneider, Volker}, year={2019}, - booktitle={The {P}algrave Handbook of Methods for Media Policy Research}, + booktitle={The {P}algrave {H}andbook of Methods for Media Policy Research}, editor={Van den Bulck, Hilde and Puppis, Manuel and Donders, Karen and Van Audenhove, Leo}, pages={471--491}, publisher={Palgrave Macmillan}, @@ -1335,6 +1402,17 @@ @incollection{koebele2020modeling doi={10.1007/978-3-030-46769-2_7} } +@article{komala2024jejaring, + title={Jejaring Wacana pada Debat Kebijakan Publik di {DPR RI}: Isu Perlindungan Data Pribadi}, + author={Komala, Ratna}, + journal={Jurnal Komunikasi Global}, + volume={13}, + number={1}, + pages={175--198}, + year={2024}, + doi={10.24815/jkg.v13i1.36954} +} + @mastersthesis{kooistra2022space, title={Space Security and Orbital Sustainability. {A}n Institutional Logics Approach}, author={Kooistra, Max Nathan}, @@ -1423,6 +1501,17 @@ @article{kukkonen2018international doi={10.1016/j.envsci.2017.12.008} } +@article{kurniawati2024analisis, + title={Analisis Jejaring Wacana {R}ancangan {U}ndang-{U}ndang {K}esehatan {T}ahun 2023 dan Potensi {J}udicial {R}eview ke {M}ahkamah {K}onstitusi}, + author={Kurniawati, Heryna Oktaviana}, + journal={UNES Law Review}, + volume={6}, + number={3}, + pages={8660--8675}, + year={2024}, + doi={10.31933/unesrev.v6i3} +} + @inproceedings{lapesa2020debatenet, title={{DE}bate{N}et-mig15: Tracing the 2015 Immigration Debate in {G}ermany over Time}, author={Lapesa, Gabriella and Blessing, Andr{\'e} and Blokker, Nico and Dayan{\i}k, Erenay and Haunss, Sebastian and Kuhn, Jonas and Pad{\'o}, Sebastian}, @@ -1493,7 +1582,7 @@ @incollection{leifeld2017discourse title={Discourse Network Analysis: Policy Debates as Dynamic Networks}, author={Leifeld, Philip}, year={2017}, - booktitle={The {O}xford Handbook of Political Networks}, + booktitle={The {O}xford {H}andbook of Political Networks}, editor={Victor, Jennifer Nicoll and Montgomery, Alexander H. and Lubell, Mark N.}, publisher={Oxford University Press}, address={Oxford}, @@ -1553,6 +1642,15 @@ @mastersthesis{leipold2016oekonomische url={https://nbn-resolving.org/urn:nbn:de:hbz:708-dh324} } +@phdthesis{leipold2024einfluss, + title={Der {E}influss von {D}iskurskoalitionen auf die {B}esteuerung von {U}nternehmen. {E}in {V}ergleich von {P}ressedebatten und {A}nh{\"o}rungen im {D}eutschen {B}undestag zur {S}teuergesetzgebung seit 1965}, + author={Leipold, Alexander}, + year={2024}, + type={Doctoral Dissertation}, + school={Leuphana Universit{\"a}t L{\"u}neburg, Fakult{\"a}t Staatswissenschaften}, + doi={10.48548/pubdata-240} +} + @article{lemke2023defining, title={Agenda-setting in Nascent Policy Subsystems: {I}ssue and Instrument Priorities across Venues}, author={Lemke, Nicole and Trein, Philipp and Varone, Fr{\'e}d{\'e}ric}, @@ -1653,6 +1751,7 @@ @misc{malkamaki2023complex howpublished={arXiv:2308.14422}, doi={10.48550/arXiv.2308.14422} } + @article{malkamaki2021acoustics, title={On the Acoustics of Policy Learning: Can Co-Participation in Policy Forums Break Up Echo Chambers?}, author={Malkam{\"a}ki, Arttu and Wagner, Paul M. and Brockhaus, Maria and Toppinen, Anne and Yl{\"a}-Anttila, Tuomas}, @@ -1720,8 +1819,17 @@ @article{markard2021analyzing doi={10.1016/j.eist.2021.08.001} } +@article{mastroianni2024crises, + title={How Do Crises Affect Policy Subsystems? {T}he Evolution of Policy Core Beliefs in the {EU} Asylum Policy}, + author={Mastroianni, Laura}, + journal={JCMS: Journal of Common Market Studies}, + year={2024}, + note={Forthcoming}, + doi={10.1111/jcms.13615} +} + @mastersthesis{mayer2022new, - title={New West Tension and Threatened Species Protection: The Western {J}oshua Tree Conservation Debate in the {M}orongo {B}asin, California}, + title={New West Tension and Threatened Species Protection: The Western {J}oshua Tree Conservation Debate in the {M}orongo {B}asin, {C}alifornia}, author={Mayer, Cameron D.}, type={Master's Thesis}, school={California State University, Long Beach, Department of Geography}, @@ -2045,7 +2153,7 @@ @article{osei2023analysing journal={Policy \& Politics}, volume={51}, number={1}, - pages={71–-90}, + pages={71--90}, year={2023}, publisher={Policy Press}, doi={10.1332/030557322X16651632139992} @@ -2116,6 +2224,27 @@ @incollection{piereder2022ideology doi={10.4324/9781003026754-19} } +@book{plumer2024kongruentes, + title={{K}ongruentes {P}olicy-{L}ernen als lernbedingter {P}olicy-{W}andel. {Z}um {K}oordinationsmechanismus des {P}olicy-{L}ernens in {R}egierungsformationen}, + author={Pl{\"u}mer, Sandra}, + year={2024}, + series={{S}tudien der {NRW} {S}chool of {G}overnance}, + publisher={Springer VS}, + address={Wiesbaden}, + doi={10.1007/978-3-658-44122-7} +} + +@article{pomiguev2024not, + title={``Not by Deed, but by Word'': A Discourse Network Analysis of Parliamentary Discussions of the Bill on Raising the Retirement Age in {R}ussia}, + author={Pomiguev I. A. and Alekseev, D. V. and Zaripov, N. A.}, + year={2024}, + journal={Monitoring of Public Opinion: Economic and Social Changes}, + number={2}, + pages={116--140}, + note={In Russian}, + doi={10.14515/monitoring.2024.2.2495} +} + @article{pop2023microdosing, title={Microdosing Psychedelics: {T}he Segregation of Spiritual and Scientific Narratives within the r/microdosing Online Community}, author={Pop, Ioana and Gielens, Erwin and Kottmann, Hannah}, @@ -2124,7 +2253,7 @@ @article{pop2023microdosing number={2}, pages={119--128}, year={2023}, - doi={10.1556/2054.2023.00014} + doi={10.1556/2054.2023.00260} } @mastersthesis{pospivsilova2022liberty, @@ -2314,6 +2443,17 @@ @article{rychlik2021come doi={10.1111/polp.12431} } +@article{samatan2024critical, + title={Critical Discourse Network Analysis on {F}erdy {S}ambo News in Online Media Using Agenda Setting Theory}, + author={Samatan, Nuriyati and Nurhasanah, Nurhasanah and Londol, Marwan and Robingah, Robingah and Fania, Salwa}, + journal={Jurnal Komunikasi Ikatan Sarjana Komunikasi Indonesia}, + volume={9}, + number={1}, + pages={146--167}, + year={2024}, + doi={10.25008/jkiski.v9i1.970} +} + @article{saputro2023media, title={Bias Media Mengenai Pemberitaan Batalnya {I}ndonesia Menjadi Tuan Rumah Piala Dunia U-20 2023 di Media Online Tirto.id dan Tempo.co}, author={Saputro, Nugroho P. and Pearly, Claire and Hidayat, Elguarddine and Charoline, Elisabeth and Wulan, Roro R.}, @@ -2493,6 +2633,15 @@ @article{sconfienza2023discourse doi={10.1057/s41253-023-00215-2} } +@article{sconfienza2024contested, + title={Contested Nuclear Discourses during the 2022 {F}rench Presidential Campaign}, + author={Sconfienza, Umberto Mario and Durand, Fr{\'e}d{\'e}ric}, + journal={Journal of Environmental Policy \& Planning}, + year={2024}, + note={Forthcoming}, + doi={10.1080/1523908X.2024.2358505} +} + @phdthesis{seidl2021ideas, title={Ideas, Politics, and Technological Change: Essays on the Comparative Political Economy of Digital Capitalism}, author={Seidl, Timo}, @@ -2866,7 +3015,7 @@ @phdthesis{tolstukha2022stalemate @incollection{tosun2016politics, title={The Politics of Hydraulic Fracturing in {G}ermany: Party Competition at Different Levels of Government}, author={Tosun, Jale and Lang, Achim}, - booktitle={Policy Debates on Hydraulic Fracturing. {C}omparing Coalition Politics in North America and Europe}, + booktitle={Policy Debates on Hydraulic Fracturing. {C}omparing Coalition Politics in {N}orth {A}merica and {E}urope}, pages={177--200}, year={2016}, publisher={Palgrave Macmillan}, @@ -2888,7 +3037,7 @@ @article{tosun2017mobilization } @mastersthesis{tribulova2019postoj, - title={Postoj \v{C}eskej republiky k energetickej tranz{\'i}cii v kontexte Energiewende –- anal{\'y}za politick{\'e}ho diskurzu}, + title={Postoj \v{C}eskej republiky k energetickej tranz{\'i}cii v kontexte Energiewende -- anal{\'y}za politick{\'e}ho diskurzu}, author={Tribulov{\'a}, Zuzana}, year={2019}, type={Master's Thesis}, @@ -2923,6 +3072,16 @@ @mastersthesis{vankova2019tavzba url={https://is.muni.cz/th/zr7zl/} } +@book{vantaggiato2024governing, + title={Governing Sea Level Rise in a Polycentric System. {E}asier Said than Done}, + author={Vantaggiato, Francesca Pia and Lubell, Mark N.}, + year={2024}, + publisher={Cambridge University Press}, + address={Cambridge}, + series={Cambridge Elements}, + doi={10.1017/9781009433594} +} + @article{vaughan2020talking, title={Talking about Tax: The Discursive Distance between 38 {D}egrees and {G}et{U}p}, author={Vaughan, Michael}, @@ -2956,7 +3115,7 @@ @article{vesa2020quiet } @article{vogeler2022integration, - title={The Integration of Environmental Objectives in the Common Agricultural Policy—Partisan Politics in the {E}uropean {P}arliament}, + title={The Integration of Environmental Objectives in the Common Agricultural Policy -- Partisan Politics in the {E}uropean {P}arliament}, author={Vogeler, Colette S.}, journal={Zeitschrift f{\"u}r Vergleichende Politikwissenschaft}, volume={15}, @@ -2993,10 +3152,11 @@ @article{volker2024discourse title={Discourse Networks of the Far Right: How Far-Right Actors Become Mainstream in Public Debates}, author={V{\"o}lker, Teresa and Saldivia Gonzatti, Daniel}, journal={Political Communication}, - pages={1--20}, year={2024}, - doi={10.1080/10584609.2024.2308601}, - note={Forthcoming} + pages={353--372}, + volume={41}, + number={3}, + doi={10.1080/10584609.2024.2308601} } @article{von2021plenardebatten, @@ -3140,6 +3300,15 @@ @phdthesis{wang2018dynamic url={http://hdl.handle.net/10871/33375} } +@book{wang2024communicating, + title={Communicating Climate Change in {C}hina. {A} Dynamic Discourse Approach}, + author={Wang, Sidan}, + year={2024}, + publisher={Palgrave Macmillan}, + address={Cham}, + doi={10.1007/978-981-97-2515-1} +} + @article{wang2017unfolding, title={Unfolding Policies for Innovation Intermediaries in {C}hina: A Discourse Network Analysis}, author={Wang, Chadwick and Wang, Luhao}, @@ -3186,13 +3355,15 @@ @article{wang2020understanding doi={10.14507/epaa.28.4451} } -@article{wesche2023influence, +@article{wesche2024influence, title={The Influence of Visions on Cooperation among Interest Organizations in Fragmented Socio-Technical Systems}, - author={Wesche, J. P. and Negro, S. O. and Brugger, H. I. and Eichhammer, W. and Hekkert, M. P.}, + author={Wesche, Julius P. and Negro, Simona O. and Brugger, Heike I. and Eichhammer, Wolfgang and Hekkert, Marko P.}, journal={Environmental Policy and Governance}, - year={2023}, - doi={10.1002/eet.2070}, - note={Forthcoming} + year={2024}, + volume={34}, + number={2}, + pages={152--165}, + doi={10.1002/eet.2070} } @article{westenberger2022soders, @@ -3219,6 +3390,17 @@ @article{wibisono2023contestation doi={10.1111/dpr.12650} } +@article{witkowski2024using, + title={Using Collaborative Governance to Regulate Sober Living Facilities: Structures and Strategies for Mitigating the Influence of Powerful Actors in Multi-Sectoral Networks}, + author={Witkowski, Kaila and Whetsell, Travis A. and Ganapati, N. Emel}, + journal={Administration \& Society}, + volume={56}, + number={4}, + pages={473--510}, + year={2024}, + doi={10.1177/00953997241235102} +} + @article{wu2020deception, title={Deception Detection Methods Incorporating Discourse Network Metrics in Synchronous Computer-Mediated Communication}, author={Wu, Jiang and Liu, Yangyang}, @@ -3243,12 +3425,13 @@ @article{wu2015dobnet doi={10.1080/0144929X.2015.1016116} } -@article{yan2023college, +@article{yan2024college, title={The College Admission Policy Evolution from 2003 to 2020 in {C}hina -- A Social Network Analysis}, author={Yan, Kun and Wu, Han and Bu, Kaiming and Wu, Lingli}, - year={2023}, + year={2024}, journal={Higher Education Policy}, - note={Forthcoming}, + volume={37}, + pages={209--236}, doi={10.1057/s41307-022-00300-1} } @@ -3274,12 +3457,14 @@ @article{you2021exploring doi={10.1111/polp.12442} } -@article{yordy2023exploring, +@article{yordy2024exploring, title={Exploring Emotional Discourses: The Case of {COVID}-19 Protests in the {US} Media}, author={Yordy, Jill and Durnov{\'a}, Anna and Weible, Christopher M.}, journal={Administrative Theory \& Praxis}, - note={Forthcoming}, - year={2023}, + year={2024}, + volume={46}, + number={1}, + pages={35--54}, doi={10.1080/10841806.2023.2176074} } @@ -3314,7 +3499,7 @@ @article{you2021comparing year={2021}, volume={38}, number={1}, - pages={107–-129}, + pages={107--129}, doi={10.1177/09520767211036800} } diff --git a/build/bibliography.md b/build/bibliography.md index 058cb510..0baf2edf 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-02-28 +date: 2024-07-16 title: "Discourse Network Analysis: Bibliography" --- @@ -21,7 +21,7 @@ before contributing any citation entries. ------------------------------------------------------------------------
+line-spacing="2">
@@ -61,9 +61,18 @@ el gran desafío* (pp. 224–251). Dykinson.
+
+ +5\. Amrihani, H. A., Eriyanto, E., & Zulkifli, M. Y. (2024). Debate on +rice import policy in Indonesia: Discourse network analysis in online +media. *Jurnal Komunikasi Ikatan Sarjana Komunikasi Indonesia*, *9*(1), +118–131. + +
+
-5\. Anshori, M., Pawito, Kartono, D. T., & Hastjarjo, S. (2023). Who +6\. Anshori, M., Pawito, Kartono, D. T., & Hastjarjo, S. (2023). Who says what? The role of the actor’s political position in ideograph construction. *Jurnal Komunikasi: Malaysian Journal of Communication*, *39*(2), 354–372. @@ -72,7 +81,7 @@ construction. *Jurnal Komunikasi: Malaysian Journal of Communication*,
-6\. Anwar, M. K., Zauhar, S., & Wanusmawatie, I. (2023). Dynamic +7\. Anwar, M. K., Zauhar, S., & Wanusmawatie, I. (2023). Dynamic governance based educational tourism development model in Indonesia. *Erudio Journal of Educational Innovation*, *10*(2), 165–175. @@ -81,7 +90,7 @@ governance based educational tourism development model in Indonesia.
-7\. Arifianto, C., Tajuddien, R., Kustini, E., & Putri, S. (2024). A +8\. Arifianto, C., Tajuddien, R., Kustini, E., & Putri, S. (2024). A discourse network analysis: How are freelancers in Indonesia portrayed? *Journal of Business Management and Economic Development*, *2*(1), 397–406. @@ -90,25 +99,43 @@ discourse network analysis: How are freelancers in Indonesia portrayed?
-8\. Aryal, K., Laudari, H. K., Maraseni, T., & Pathak, B. R. (2022). +9\. Aryal, K., Laudari, H. K., Maraseni, T., & Pathak, B. R. (2022). Navigating policy debates of and discourse coalitions on Nepal’s scientific forest management. *Forest Policy and Economics*, *141*, 102768.
+
+ +10\. Astner, K., & KĂŒtt, M. (2024). The Treaty on the Prohibition of +Nuclear Weapons. In U. KĂŒhn (Ed.), *Germany and nuclear weapons in the +21st century. Atomic Zeitenwende?* (pp. 203–229). Routledge. + + +
+
-9\. Békgaard, A. S., Engberg, C., & Hasselbalch, J. A. (2024). The +11\. Békgaard, A. S., Engberg, C., & Hasselbalch, J. A. (2024). The technocentric consensus: A discourse network analysis of the European -circular economy debate. *Journal of Environmental Policy & Planning*. - +circular economy debate. *Journal of Environmental Policy & Planning*, +*26*(2), 173–187. + +
+ +
+ +12\. Bafadhal, O. M., Saraswaty, E., & Handoko, A. I. (2024). Konflik +dan wacana media dalam pembangunan gereja di Palembang: Analisis +jaringan diskursus. *Jurnal Studi Komunikasi*, *8*(1), 217–226. +
-10\. Bandau, S. L. (2021). *Emerging institutions in the global space +13\. Bandau, S. L. (2021). *Emerging institutions in the global space sector. An institutional logics approach* \[Master's Thesis, Utrecht University, Copernicus Institute of Sustainable Development\]. @@ -117,7 +144,7 @@ University, Copernicus Institute of Sustainable Development\].
-11\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them +14\. Bandelow, N. C., & Hornung, J. (2019). One discourse to rule them all? Narrating the agenda for labor market policies in France and Germany. *Policy and Society*, *38*(3), 408–428. @@ -126,7 +153,7 @@ Germany. *Policy and Society*, *38*(3), 408–428.
-12\. Barnickel, C. (2019). *Postdemokratisierung der +15\. Barnickel, C. (2019). *Postdemokratisierung der Legitimationspolitik: Diskursnetzwerke in bundesdeutschen Großen RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS. @@ -135,7 +162,7 @@ RegierungserklĂ€rungen und Aussprachen 1949–2014*. Springer VS.
-13\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: +16\. Barnickel, C. (2020). Vorstellungen legitimen Regierens: Legitimationspolitik in der Großen RegierungserklĂ€rung der 19. Wahlperiode im Vergleich. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *14*(3), 199–223. @@ -145,7 +172,7 @@ Politikwissenschaft*, *14*(3), 199–223.
-14\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and +17\. Baulenas, E. (2021). She’s a rainbow: Forest and water policy and management integration in Germany, Spain and Sweden. *Land Use Policy*, *101*, 105182. @@ -153,7 +180,7 @@ management integration in Germany, Spain and Sweden. *Land Use Policy*,
-15\. Belova, A. (2021). *Another silver bullet for the energy +18\. Belova, A. (2021). *Another silver bullet for the energy transition? Discourse network analysis of German hydrogen debate* \[Master’s thesis, Department of International Relations; European Studies, Energy Policy Studies, Masaryk University\]. @@ -163,7 +190,7 @@ Studies, Energy Policy Studies, Masaryk University\].
-16\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & +19\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & Kemmerzell, J. (2023). The more the merrier? Actors and ideas in the evolution of German hydrogen policy discourse. *Energy Research & Social Science*, *97*, 102965. @@ -172,7 +199,7 @@ Science*, *97*, 102965.
-17\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse +20\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse network analysis as a novel approach to party control. *Politics and Governance*, *8*(2), 229–242. @@ -180,16 +207,25 @@ Governance*, *8*(2), 229–242.
-18\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a +21\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a tool to maintain party unity: The case of oppressed political conflict in German parliament debates on the Euro crisis. *Party Politics*, -*29*(3), 554--569. +*29*(3), 554–569. + +
+ +
+ +22\. Bisht, B. S. (2023). *Media discourse analysis: Solar power in +India. The case of Gujarat* \[Master's Thesis, Masaryk University, +Faculty of Social Studies, Department of International Relations; +European Studies\].
-19\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network +23\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network analysis of climate delayism in the Czech Republic* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. @@ -198,7 +234,7 @@ Masaryk University, Faculty of Social Studies\].
-20\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & +24\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & Padó, S. (2019). An environment for relational annotation of political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations*, 105–110. @@ -208,7 +244,7 @@ Computational Linguistics: System Demonstrations*, 105–110.
-21\. Boas, I. (2015). *Climate migration and security: Securitisation as +25\. Boas, I. (2015). *Climate migration and security: Securitisation as a strategy in climate change politics*. Routledge. @@ -216,7 +252,7 @@ a strategy in climate change politics*. Routledge.
-22\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice +26\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice o energetickej efektĂ­vnosti – analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -225,7 +261,7 @@ Thesis, Masaryk University, Faculty of Social Studies\].
-23\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual +27\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual screening: Analyzing roles, content and motivations in political Twitter conversations. *Politics and Governance*, *8*(2), 311–325. @@ -234,7 +270,7 @@ conversations. *Politics and Governance*, *8*(2), 311–325.
-24\. Brandenberger, L. (2019). Predicting network events to assess +28\. Brandenberger, L. (2019). Predicting network events to assess goodness of fit of relational event models. *Political Analysis*, *27*(4), 556–571. @@ -242,7 +278,7 @@ goodness of fit of relational event models. *Political Analysis*,
-25\. Breindl, Y. (2013). Discourse networks on state-mandated access +29\. Breindl, Y. (2013). Discourse networks on state-mandated access blocking in Germany and France. *Info*, *15*(6), 42–62. @@ -250,7 +286,7 @@ blocking in Germany and France. *Info*, *15*(6), 42–62.
-26\. Bresser, B. (2024). *Transitions in the risk assessment regime: +30\. Bresser, B. (2024). *Transitions in the risk assessment regime: Prospects from the case of animal-free testing in the cosmetics industry* \[Master's Thesis, Utrecht University, Innovation Sciences\]. @@ -259,7 +295,7 @@ industry* \[Master's Thesis, Utrecht University, Innovation Sciences\].
-27\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, +31\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, A., Chien, Y.-J., Edling, C., Fisher, D. R., Giouzepas, G., Haluza-DeLay, R., Hazegawa, K., Hirschi, C., Horta, A., Ikeda, K., Jin, J., Ku, D., Lahsen, M., Lee, H.-C., Lin, T.-L. A., 
 Zhengyi, S. (2016). @@ -271,7 +307,7 @@ Conflicting climate change frames in a global field of media discourse.
-28\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis +32\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis of climate change and society: A network approach. In M. J. Manfredo, J. J. Vaske, A. Rechkemmer, & E. A. Duke (Eds.), *Understanding society and natural resources. Forging new strands of integration across the social @@ -282,7 +318,7 @@ sciences* (pp. 203–228). Springer Open.
-29\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: +33\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: Making sense of the international discourse on transfer pricing methodologies. *Review of International Political Economy*, *29*(1), 307–335. @@ -291,7 +327,7 @@ methodologies. *Review of International Political Economy*, *29*(1),
-30\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse +34\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse networks on local energy transitions. *Environmental Innovation and Societal Transitions*, *39*, 141–154. @@ -300,7 +336,7 @@ Societal Transitions*, *39*, 141–154.
-31\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die +35\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die US-Außenpolitik. Ein Vergleich der Israelpolitik zwischen den Kabinetten Obama II und Trump* \[Master's Thesis, Leopold-Franzens-UniversitĂ€t Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\]. @@ -310,7 +346,7 @@ Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\].
-32\. Brutschin, E. (2013). *Dynamics in EU policy-making: The +36\. Brutschin, E. (2013). *Dynamics in EU policy-making: The liberalization of the European gas market* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -319,17 +355,17 @@ University of Konstanz, Department of Politics; Public Administration\].
-33\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A +37\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A discourse network analysis of UK newspaper coverage of the “sugar tax” -debate before and after the announcement of the soft drinks industry -levy. *BMC Public Health*, *19*(490), 1–14. +debate before and after the announcement of the Soft Drinks Industry +Levy. *BMC Public Health*, *19*(490), 1–14.
-34\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of +38\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of territoriality in policy networks. The case of transportation policies in the cross-border metropolitan regions of Brussels and Luxembourg* \[Doctoral Dissertation, KU Leuven, Division of Geography; Tourism\]. @@ -339,7 +375,7 @@ in the cross-border metropolitan regions of Brussels and Luxembourg*
-35\. Černỳ, O. (2018). *Limity českĂ© +39\. Černỳ, O. (2018). *Limity českĂ© energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -348,7 +384,7 @@ energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­*
-36\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media +40\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media coverage: A case of the Czech coal policy. *Politics and Governance*, *8*(2), 272–285. @@ -356,7 +392,7 @@ coverage: A case of the Czech coal policy. *Politics and Governance*,
-37\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej +41\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej politike Českej republiky* \[Master's Thesis, Masarykova univerzita, Fakulta sociĂĄlnı́ch studiı́\]. @@ -364,7 +400,7 @@ Fakulta sociĂĄlnı́ch studiı́\].
-38\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green +42\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green networks in the Basque country between 2007 and 2017* \[PhD thesis, University of Trento, School of Social Sciences\]. @@ -373,15 +409,15 @@ University of Trento, School of Social Sciences\].
-39\. Clasing-Manquian, P. (2024). How free tuition cecame a policy in +43\. Clasing-Manquian, P. (2024). How free tuition cecame a policy in Chile: The importance of policy actors and their beliefs. *Higher -Education*. +Education*.
-40\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un +44\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un anĂĄlisis desde las coaliciones discursivas* \[Tesis Postgrado, Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\]. @@ -390,7 +426,7 @@ Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\].
-41\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking +45\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking in unison? Explaining the role of agenda-setter constellations in the ECB policy agenda using a network-based approach. *Journal of European Public Policy*. @@ -399,17 +435,17 @@ Public Policy*.
-42\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). -Depicting mangrove’s potential as blue carbon champion in indonesia. In +46\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). +Depicting mangrove’s potential as blue carbon champion in Indonesia. In R. Dasgupta, S. Hashimoto, & O. Saito (Eds.), *Assessing, mapping and -modelling of mangrove ecosystem services in the asia-pacific region* +modelling of mangrove ecosystem services in the Asia-Pacific region* (pp. 167–181). Springer.
-43\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse +47\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse network analysis of the political debate about the German passenger car toll act. *German Politics*. @@ -418,7 +454,7 @@ toll act. *German Politics*.
-44\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the +48\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the climate change arena in Mexico: A network analysis. *Journal of Environmental Management*, *215*, 239–247. @@ -427,7 +463,7 @@ Environmental Management*, *215*, 239–247.
-45\. Drozdzynski, F. A. (2022). *The common agricultural policy post +49\. Drozdzynski, F. A. (2022). *The Common Agricultural Policy post 2020: An analysis of the beliefs of selected key stakeholders* \[Master's Thesis, University of Twente, Faculty of Behavioural, Management; Social Sciences\]. @@ -436,15 +472,15 @@ Management; Social Sciences\].
-46\. Durel, L. (2024). Border carbon adjustment compliance and the WTO: +50\. Durel, L. (2024). Border carbon adjustment compliance and the WTO: The interactional evolution of law. *Journal of International Economic -Law*. +Law*, *27*(1), 18–40.
-47\. Duygan, M. (2018). *An actor-based analysis of political context +51\. Duygan, M. (2018). *An actor-based analysis of political context for supporting sustainability transitions of socio-technical systems: A study of Swiss waste management* \[Doctoral Dissertation, ETH ZĂŒrich, Department of Environmental Systems Science\]. @@ -454,7 +490,7 @@ Department of Environmental Systems Science\].
-48\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for +52\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for conceptualizing and uncovering the determinants of agency in socio-technical transitions. *Environmental Innovation and Societal Transitions*, *33*, 13–29. @@ -463,7 +499,7 @@ Transitions*, *33*, 13–29.
-49\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse +53\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse coalitions in Swiss waste management: Gridlock or winds of change? *Waste Management*, *72*, 25–44. @@ -472,7 +508,7 @@ coalitions in Swiss waste management: Gridlock or winds of change?
-50\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes +54\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes agency? Determinants of actors’ influence on formal institutions in Swiss waste management. *Technological Forecasting and Social Change*, *162*, 120413. @@ -481,26 +517,26 @@ Swiss waste management. *Technological Forecasting and Social Change*,
-51\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How +55\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How discourse network analysis (DNA) can help CSR research to investigate the “new” political role of corporations. In M. Nagel, P. Kenis, P. -Leifeld, & H.-J. Schmedes (Eds.), *Politische komplexitĂ€t, governance -von innovationen und policy-netzwerke* (pp. 139–146). Springer VS. +Leifeld, & H.-J. Schmedes (Eds.), *Politische KomplexitĂ€t, Governance +von Innovationen und Policy-Netzwerke* (pp. 139–146). Springer VS.
-52\. Eder, F. (2015). *Der irakkrieg 2003*. Innsbruck University Press. +56\. Eder, F. (2015). *Der Irakkrieg 2003*. Innsbruck University Press.
-53\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. -Ostermann (Eds.), *Routledge handbook of foreign policy analysis +57\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. +Ostermann (Eds.), *Routledge Handbook of foreign policy analysis methods* (pp. 516–535). Taylor & Francis. @@ -508,7 +544,7 @@ methods* (pp. 516–535). Taylor & Francis.
-54\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, +58\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*, *15*(1), 21–42. @@ -516,7 +552,7 @@ discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*,
-55\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting +59\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting counter-terrorism: Discourse networks and the politicisation of counter-terrorism in Austria. *Journal of International Relations and Development*, *24*(1), 171–195. @@ -526,7 +562,7 @@ Development*, *24*(1), 171–195.
-56\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan +60\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan komunikator publik dalam berita omicron baru di media online. *Jurnal Riset Komunikasi*, *6*(1), 58–79. @@ -535,7 +571,7 @@ Riset Komunikasi*, *6*(1), 58–79.
-57\. Elislah, N. (2023). Discourse network analysis on delaying +61\. Elislah, N. (2023). Discourse network analysis on delaying elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2), 225–240. @@ -543,7 +579,7 @@ elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2),
-58\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue +62\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue debate: A study on Covid-19 cases in Indonesia. *Jurnal Komunikasi: Malaysian Journal of Communication*, *36*(3), 209–227. @@ -552,7 +588,7 @@ Malaysian Journal of Communication*, *36*(3), 209–227.
-59\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping +63\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping discourse coalitions in the minimum unit pricing for alcohol debate: A discourse network analysis of UK newspaper coverage. *Addiction*, *114*(4), 741–753. @@ -561,7 +597,7 @@ discourse network analysis of UK newspaper coverage. *Addiction*,
-60\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological +64\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological tensions in education policy networks: An analysis of the policy innovators in education network in the United States. *Foro de Educacion*, *19*(1), 11–28. @@ -570,18 +606,18 @@ Educacion*, *19*(1), 11–28.
-61\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban +65\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban food strategies. In C. Tornaghi (Ed.), *Re-imagining sustainable food planning, building resourcefulness: Food movements, insurgent planning and heterodox economics: Proceedings of the 8th annual conference AESOP sustainable food planning group* (pp. 79–88). Coventry University. -https://doi.org/ +
-62\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate +66\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate policy blockage. *Climatic Change*, *155*(4), 469–487. @@ -589,7 +625,7 @@ policy blockage. *Climatic Change*, *155*(4), 469–487.
-63\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the +67\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the ideological networks of American climate politics. *Climatic Change*, *116*(3), 523–545. @@ -597,16 +633,16 @@ ideological networks of American climate politics. *Climatic Change*,
-64\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does -political polarization come from? Locating polarization within the US -climate change debate. *American Behavioral Scientist*, *57*(1), 70–92. +68\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does +political polarization come from? Locating polarization within the U.S. +Climate change debate. *American Behavioral Scientist*, *57*(1), 70–92.
-65\. Friis, G. (2020). *Populist radical right parties into parliament: +69\. Friis, G. (2020). *Populist radical right parties into parliament: Changes in mainstream parties’ political positions in parliamentary debates on immigration and refugees* \[Master’s thesis, Uppsala University, Disciplinary Domain of Humanities; Social Sciences, Faculty @@ -617,7 +653,7 @@ of Social Sciences, Department of Government\].
-66\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network +70\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network analysis to understand ideological convergence and change in educational subsystems. *Education Policy Analysis Archives*, *28*(118). @@ -626,7 +662,7 @@ subsystems. *Education Policy Analysis Archives*, *28*(118).
-67\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). +71\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). Building consensus: Idea brokerage in teacher policy networks. *American Educational Research Journal*, *57*(2), 872–905. @@ -635,7 +671,7 @@ Educational Research Journal*, *57*(2), 872–905.
-68\. Galli Robertson, A. M. (2021). Privileged accounts in the debate +72\. Galli Robertson, A. M. (2021). Privileged accounts in the debate over coal-fired power in the United States. *Society & Natural Resources*, *34*(2), 188–207. @@ -644,7 +680,7 @@ Resources*, *34*(2), 188–207.
-69\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable +73\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable country: Agenda-setting and a discourse network approach on Philippine broadsheet media* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -654,7 +690,7 @@ Science, Oeschger Centre for Climate Change Research\].
-70\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The +74\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The politics of climate finance: Consensus and partisanship in designing green state investment banks in the United Kingdom and Australia. *Energy Research & Social Science*, *69*, 101583. @@ -664,7 +700,7 @@ green state investment banks in the United Kingdom and Australia.
-71\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local +75\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local policymakers’ attitudes towards climate change: A multi-method case study. *Social Networks*, *25*, 197–209. @@ -673,7 +709,7 @@ study. *Social Networks*, *25*, 197–209.
-72\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates +76\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates and agricultural policies: Discourse coalitions behind the creation of Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80. @@ -682,7 +718,7 @@ Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80.
-73\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social +77\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social innovation: Analysing policy debates in Japan. *Journal of Social Entrepreneurship*. @@ -690,7 +726,7 @@ Entrepreneurship*.
-74\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate +78\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate change in Italy: A discourse network analysis. *Politics and Governance*, *8*(2), 215–228. @@ -698,7 +734,7 @@ Governance*, *8*(2), 215–228.
-75\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and +79\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and right: A discourse network analysis of universal basic income on Dutch Twitter. *Journal of Social Policy*. @@ -707,15 +743,24 @@ Twitter. *Journal of Social Policy*.
-76\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage +80\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4), 490–514.
+
+ +81\. Goritz, A., & Kolleck, N. (2024). Education in international +climate pledges – identifying education framings in countries nationally +determined contributions (NDCs). *Environmental Education Research*. + + +
+
-77\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of +82\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of cultural resistance to a speed limit on German highways* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -724,7 +769,7 @@ Development\].
-78\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, +83\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, C. (2022). Discourse network analysis of nuclear narratives. In M. D. Jones, M. K. McBeth, & E. Shanahan (Eds.), *Narratives and the policy process: Applications of the narrative policy framework* (pp. 13–38). @@ -734,7 +779,7 @@ Montana State University Library.
-79\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations +84\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations of the mexican power generation policy subsystem. In A.-M. Bercu, I. Bilan, & C.-M. Apostoaie (Eds.), *European administrative area: Integration and resilience dynamics. Proceedings of the international @@ -746,7 +791,7 @@ Ioan Cuza” din Iai.
-80\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y +85\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y decisiones: La polĂ­tica de generaciĂłn elĂ©ctrica en mĂ©xico 1994–2018* \[PhD thesis, Centro de InvestigaciĂłn y Docencia EconĂłmicas (CIDE), Doctorado en PolĂ­ticas PĂșblicas\]. @@ -755,7 +800,7 @@ Doctorado en PolĂ­ticas PĂșblicas\].
-81\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and +86\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and policy change in Mexican electricity policy: A discourse network analysis. *Policy & Politics*. @@ -764,7 +809,7 @@ analysis. *Policy & Politics*.
-82\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on +87\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on the revision of Indonesian information and electronic transaction law. *Jurnal Studi Komunikasi*, *7*(2), 519–538. @@ -773,7 +818,7 @@ the revision of Indonesian information and electronic transaction law.
-83\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics +88\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics after the “great recession.” Investigating the impact of economic shocks on EU climate policy-making in three case studies* \[Doctoral Dissertation, University of Potsdam, Faculty of Economics; Social @@ -783,7 +828,7 @@ Sciences\].
-84\. Hanschmann, R. (2017). Polarized business interests. EU climate +89\. Hanschmann, R. (2017). Polarized business interests. EU climate policy-making during the “great recession.” In D. K. Jesuit & R. A. Williams (Eds.), *Public policy, governance and polarization. Making governance work* (1st ed., pp. 126–156). Routledge. @@ -793,7 +838,7 @@ governance work* (1st ed., pp. 126–156). Routledge.
-85\. Hasselbalch, J. (2017). *The contentious politics of disruptive +90\. Hasselbalch, J. (2017). *The contentious politics of disruptive innovation: Vaping and fracking in the European Union* \[PhD thesis, University of Warwick, Department of Politics; International Studies; Université Libre de Bruxelles, Département de Sciences Politiques\]. @@ -803,7 +848,7 @@ Université Libre de Bruxelles, Département de Sciences Politiques\].
-86\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity +91\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity and skills in European labor mobility. *Review of International Political Economy*, *26*(6), 1333–1360. @@ -812,7 +857,7 @@ Political Economy*, *26*(6), 1333–1360.
-87\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke +92\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau (Eds.), *Capitalism and its legitimacy in times of crisis* (pp. 191–220). Palgrave Macmillan. @@ -822,7 +867,7 @@ without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau
-88\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der +93\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der Atomenergie: Diskursnetzwerkanalyse als Beitrag zur ErklĂ€rung einer radikalen Politikwende. *Zeitschrift fĂŒr Diskursforschung / Journal for Discourse Studies*, *1*(3), 288–316. @@ -832,7 +877,7 @@ Discourse Studies*, *1*(3), 288–316.
-89\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of +94\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of political discourse dynamics and the case of Germany’s nuclear energy phase-out. *Network Science*, *11*(2), 205–223. @@ -841,7 +886,7 @@ phase-out. *Network Science*, *11*(2), 205–223.
-90\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, +95\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, E., & Lapesa, G. (2020). Integrating manual and automatic annotation for the creation of discourse network data sets. *Politics and Governance*, *8*(2), 326–339. @@ -850,7 +895,7 @@ the creation of discourse network data sets. *Politics and Governance*,
-91\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). +96\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). Finanzkrise ohne Legitimationskrise? Kapitalismuskritik in der deutschen QualitĂ€tspresse. In M. Dammayr, D. Grass, & B. RothmĂŒller (Eds.), *LegitimitĂ€t. Gesellschaftliche, politische und wissenschaftliche @@ -861,7 +906,7 @@ Bruchlinien in der Rechtfertigung* (pp. 73–94). Transcript.
-92\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive +97\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive practices and the “need” for positive behaviour support (PBS): A critical discourse examination of disability policy beliefs. *Journal of Intellectual Disabilities*, *27*(1), 170–189. @@ -869,9 +914,18 @@ Intellectual Disabilities*, *27*(1), 170–189.
+
+ +98\. Heermann, M. (2024). *Preferences and coalitions in European Union +internet policy* \[Doctoral Dissertation, University of Konstanz, +Department of Politics; Public Administration\]. + + +
+
-93\. Heiberg, J. (2022). *The geography of configurations that work* +99\. Heiberg, J. (2022). *The geography of configurations that work* \[Doctoral Dissertation, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -879,7 +933,7 @@ Sustainable Development\].
-94\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of +100\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of technology legitimation: How multiscalar institutional dynamics matter for path creation in emerging industries. *Economic Geography*, *96*(5), 470–498. @@ -888,7 +942,7 @@ for path creation in emerging industries. *Economic Geography*, *96*(5),
-95\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions +101\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions through socio-technical configuration analysis – a methodological framework and a case study in the water sector. *Research Policy*, *51*(1), 104363. @@ -897,7 +951,7 @@ framework and a case study in the water sector. *Research Policy*,
-96\. Heinmiller, T. B. (2023). Have advocacy coalitions been +102\. Heinmiller, T. B. (2023). Have advocacy coalitions been difference-making in Canadian policy processes? Evidence from firearms policy processes in the 1970s and 1990s. *Canadian Political Science Review*, *17*(2), 1–17. @@ -907,17 +961,17 @@ Review*, *17*(2), 1–17.
-97\. Henrichsen, T. (2020). *Party competition as interdependent process -– assessing the contagion effect of Eurosceptic parties in Italy* \[PhD -thesis, Sant’Anna School of Advanced Studies Pisa, Joint PhD in -Political Science, European Politics; International Relations\]. +103\. Henrichsen, T. (2020). *Party competition as interdependent +process – assessing the contagion effect of Eurosceptic parties in +Italy* \[PhD thesis, Sant’Anna School of Advanced Studies Pisa, Joint +PhD in Political Science, European Politics; International Relations\].
-98\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global +104\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global governance: Intersecting politicizations and the reform of investor–state arbitration. *Politics and Governance*, *8*(1), 336–347. @@ -926,7 +980,7 @@ investor–state arbitration. *Politics and Governance*, *8*(1), 336–347.
-99\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in +105\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in der Plattformökonomie. Herausforderungen des “coalition building” bei der Kooperation zwischen IG Metall und YouTubers Union. *Zeitschrift fĂŒr Soziologie*, *51*(2), 174–192. @@ -935,7 +989,7 @@ Soziologie*, *51*(2), 174–192.
-100\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, +106\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, P. (2020). Policy congruence and advocacy strategies in the discourse networks of minimum unit pricing for alcohol and the soft drinks industry levy. *Addiction*, *115*(12), 2303–2314. @@ -945,7 +999,7 @@ industry levy. *Addiction*, *115*(12), 2303–2314.
-101\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing +107\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing states’ messages about common core instruction: An analysis of English/language arts and close reading resources. *Teachers College Record*, *122*(3), 1–42. @@ -954,7 +1008,7 @@ Record*, *122*(3), 1–42.
-102\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: +108\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -963,7 +1017,7 @@ Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta*
-103\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in +109\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in Canada’s parliamentary system* \[PhD thesis, Simon Fraser University, Department of Political Science\]. @@ -971,7 +1025,7 @@ Department of Political Science\].
-104\. Horning, D. G. (2017). *Understanding structure and character in +110\. Horning, D. G. (2017). *Understanding structure and character in rural water governance networks* \[PhD thesis, University of British Columbia, College of Graduate Studies\]. @@ -979,7 +1033,7 @@ Columbia, College of Graduate Studies\].
-105\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). +111\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). Programmatisches Handeln in der deutschen Verkehrspolitik. Gemeinsame IdentitĂ€ten von Akteuren im Umfeld des Deutschlandtakts. In D. Sack, H. Straßheim, & K. Zimmermann (Eds.), *Renaissance der Verkehrspolitik. @@ -990,7 +1044,7 @@ Springer VS.
-106\. Howe, A. C. (2022). *Network processes related to political +112\. Howe, A. C. (2022). *Network processes related to political discourse and policy positions: The case of climate change policy networks in Canada* \[PhD thesis, University of British Columbia, Department of Sociology\]. @@ -999,7 +1053,7 @@ Department of Sociology\].
-107\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media +113\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media coverage and perceived policy influence of environmental actors: Good strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310. @@ -1008,7 +1062,7 @@ strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310.
-108\. Hullmann, C. (2023). *Case study on the German discourse of +114\. Hullmann, C. (2023). *Case study on the German discourse of industry decarbonization* \[Master's Thesis, Radboud University Nijmegen, Nijmegen School of Management\]. @@ -1017,7 +1071,7 @@ Nijmegen, Nijmegen School of Management\].
-109\. Hurka, S., & Nebel, K. (2013). Framing and policy change after +115\. Hurka, S., & Nebel, K. (2013). Framing and policy change after shooting rampages: A comparative analysis of discourse networks. *Journal of European Public Policy*, *20*(3), 390–406. @@ -1026,7 +1080,7 @@ shooting rampages: A comparative analysis of discourse networks.
-110\. Imbert, I. (2017). *An inquiry into the material and ideational +116\. Imbert, I. (2017). *An inquiry into the material and ideational dimensions of policymaking: A case study of fuel poverty in Germany* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -1036,7 +1090,7 @@ Public Administration\].
-111\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja +117\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja kannatuskoalitiot varhaiskasvatuksen diskurssiverkostossa* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -1045,7 +1099,7 @@ Thesis, University of Helsinki, Faculty of Social Sciences\].
-112\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). +118\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). Diskursnetzwerkanalyse. Überlegungen zur Theoriebildung und Methodik. In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. @@ -1055,7 +1109,7 @@ V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.),
-113\. Jeong, M. (2017). *National renewable energy policy in a global +119\. Jeong, M. (2017). *National renewable energy policy in a global world* \[PhD thesis, University of Maryland, College Park, School of Public Policy\]. @@ -1064,7 +1118,7 @@ Public Policy\].
-114\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China +120\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China have a public debate on genetically modified organisms? A discourse network analysis of public debate on Weibo. *Public Understanding of Science*, *31*(6), 732–750. @@ -1073,7 +1127,7 @@ Science*, *31*(6), 732–750.
-115\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The +121\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The injustice of procedure, distribution, and recognition within the discourse of air pollution in Delhi, India. *Environmental Sociology*, *9*(2), 176–189. @@ -1082,7 +1136,7 @@ discourse of air pollution in Delhi, India. *Environmental Sociology*,
-116\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: +122\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: Exploring risk and stigma of COVID-19 in the context of beliefs, actors and hierarchies in India. *Current Sociology*, *69*(4), 492–511. @@ -1091,7 +1145,7 @@ and hierarchies in India. *Current Sociology*, *69*(4), 492–511.
-117\. Kammerer, M. (2017). *Climate politics at the intersection between +123\. Kammerer, M. (2017). *Climate politics at the intersection between international dynamics and national decision-making: A policy network approach* \[Doctoral Thesis, University of Zurich, Faculty of Arts; Social Sciences\]. @@ -1100,7 +1154,7 @@ Social Sciences\].
-118\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die +124\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die EU: Eine Diskursperspektive auf die deutsche und schweizerische Klimapolitik. In R. Careja, P. Emmenegger, & N. Giger (Eds.), *The european social model under pressure. Liber amicorum in honour of klaus @@ -1111,7 +1165,7 @@ armingeon* (pp. 599–623). Springer VS.
-119\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate +125\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate change policy: The maturation of a policy discourse in the national and international context. *Social Networks*, *75*, 65–77. @@ -1120,7 +1174,7 @@ international context. *Social Networks*, *75*, 65–77.
-120\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat +126\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat Jakarta-Bandung dalam media online. *Jurnal Ilmu Komunikasi UHO: Jurnal Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34. @@ -1129,7 +1183,7 @@ Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34.
-121\. Keller, S. (2023). *Analysis of the media discourse about meat and +127\. Keller, S. (2023). *Analysis of the media discourse about meat and meat substitutes in U.S. Media between 2016 and 2021* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -1139,9 +1193,9 @@ Change Research\].
-122\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: +128\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: Policy network analysis. In H. Van den Bulck, M. Puppis, K. Donders, & -L. Van Audenhove (Eds.), *The Palgrave handbook of methods for media +L. Van Audenhove (Eds.), *The Palgrave Handbook of methods for media policy research* (pp. 471–491). Palgrave Macmillan. @@ -1149,7 +1203,7 @@ policy research* (pp. 471–491). Palgrave Macmillan.
-123\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme +129\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme digital dalam perdebatan isu “presiden tiga periode” di Twitter. *Jurnal Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94. @@ -1158,7 +1212,7 @@ Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94.
-124\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? +130\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? Distributed-Ledger-Technologien im politischen Diskurs. Leitbilder, Ideen und Diskursnetzwerke im deutschen Bundestag der 19. Legislaturperiode* \[Master's Thesis, FU Berlin, Otto-Suhr-Institut fĂŒr @@ -1168,7 +1222,7 @@ Politikwissenschaft\].
-125\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech +131\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech Republic: Individual responsibility or structural issue? *Energy Research & Social Science*, *72*, 101877. @@ -1177,7 +1231,7 @@ Research & Social Science*, *72*, 101877.
-126\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling +132\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling environmental governance in the Lake Tahoe basin: A multiplex network approach. In M. Fischer & K. Ingold (Eds.), *Networks in water governance* (pp. 173–202). Palgrave Macmillan. @@ -1185,9 +1239,17 @@ governance* (pp. 173–202). Palgrave Macmillan.
+
+ +133\. Komala, R. (2024). Jejaring wacana pada debat kebijakan publik di +DPR RI: Isu perlindungan data pribadi. *Jurnal Komunikasi Global*, +*13*(1), 175–198. + +
+
-127\. Kooistra, M. N. (2022). *Space security and orbital +134\. Kooistra, M. N. (2022). *Space security and orbital sustainability. An institutional logics approach* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -1196,7 +1258,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-128\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). +135\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). Animals and climate change: A visual and discourse network analysis of Instagram posts. *Environmental Sociology*, *9*(4), 409–426. @@ -1205,7 +1267,7 @@ Instagram posts. *Environmental Sociology*, *9*(4), 409–426.
-129\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors +136\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors and justifications in media debates on Arctic climate change in Finland and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117. @@ -1214,7 +1276,7 @@ and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117.
-130\. Kukkonen, A. (2018). *Discourse networks and justifications of +137\. Kukkonen, A. (2018). *Discourse networks and justifications of climate change policy: News media debates in Canada, the United States, Finland, France, Brazil and India* \[Doctoral Dissertation, University of Helsinki, Faculty of Social Sciences\]. @@ -1224,7 +1286,7 @@ of Helsinki, Faculty of Social Sciences\].
-131\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to +138\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to politicization of science: How the forestry coalition challenged the scientific consensus in the Finnish news media debate on increased logging. *Society & Natural Resources*, *37*(1), 91–112. @@ -1234,7 +1296,7 @@ logging. *Society & Natural Resources*, *37*(1), 91–112.
-132\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy +139\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy interface as a discourse network: Finland’s climate change policy 2002–2015. *Politics and Governance*, *8*(2), 200. @@ -1243,7 +1305,7 @@ interface as a discourse network: Finland’s climate change policy
-133\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy +140\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy coalitions, beliefs and climate change policy in the United States. *Public Administration*, *95*(3), 713–729. @@ -1252,7 +1314,7 @@ coalitions, beliefs and climate change policy in the United States.
-134\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., +141\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., Lahsen, M., & Stoddart, M. C. J. (2018). International organizations, advocacy coalitions, and domestication of global norms: Debates on climate change in Canada, the US, Brazil, and India. *Environmental @@ -1261,9 +1323,18 @@ Science & Policy*, *81*, 54–62.
+
+ +142\. Kurniawati, H. O. (2024). Analisis jejaring wacana Rancangan +Undang-Undang Kesehatan Tahun 2023 dan potensi Judicial Review ke +Mahkamah Konstitusi. *UNES Law Review*, *6*(3), 8660–8675. + + +
+
-135\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., +143\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., Kuhn, J., & PadĂł, S. (2020). DEbateNet-mig15: Tracing the 2015 immigration debate in Germany over time. *Proceedings of the Twelfth Language Resources and Evaluation Conference*, 919–927. @@ -1273,7 +1344,7 @@ Language Resources and Evaluation Conference*, 919–927.
-136\. Laurer, M., & Seidl, T. (2021). Regulating the European +144\. Laurer, M., & Seidl, T. (2021). Regulating the European data-driven economy: A case study on the general data protection regulation. *Policy & Internet*, *13*(2), 257–277. @@ -1282,7 +1353,7 @@ regulation. *Policy & Internet*, *13*(2), 257–277.
-137\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem +145\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem Discourse Network Analyzer (DNA). In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. 391–404). Springer VS. @@ -1292,7 +1363,7 @@ Visualisierungen* (pp. 391–404). Springer VS.
-138\. Leifeld, P. (2013). Reconceptualizing major policy change in the +146\. Leifeld, P. (2013). Reconceptualizing major policy change in the advocacy coalition framework: A discourse network analysis of German pension politics. *Policy Studies Journal*, *41*(1), 169–198. @@ -1301,7 +1372,7 @@ pension politics. *Policy Studies Journal*, *41*(1), 169–198.
-139\. Leifeld, P. (2014). Polarization of coalitions in an agent-based +147\. Leifeld, P. (2014). Polarization of coalitions in an agent-based model of political discourse. *Computational Social Networks*, *1*(1), 1–22. @@ -1309,7 +1380,7 @@ model of political discourse. *Computational Social Networks*, *1*(1),
-140\. Leifeld, P. (2016). *Policy debates as dynamic networks: German +148\. Leifeld, P. (2016). *Policy debates as dynamic networks: German pension politics and privatization discourse*. Campus. @@ -1317,9 +1388,9 @@ pension politics and privatization discourse*. Campus.
-141\. Leifeld, P. (2017). Discourse network analysis: Policy debates as +149\. Leifeld, P. (2017). Discourse network analysis: Policy debates as dynamic networks. In J. N. Victor, A. H. Montgomery, & M. N. Lubell -(Eds.), *The Oxford handbook of political networks* (pp. 301–325). +(Eds.), *The Oxford Handbook of political networks* (pp. 301–325). Oxford University Press. @@ -1327,7 +1398,7 @@ Oxford University Press.
-142\. Leifeld, P. (2020). Policy debates and discourse network analysis: +150\. Leifeld, P. (2020). Policy debates and discourse network analysis: A research agenda. *Politics and Governance*, *8*(2), 180–183. @@ -1335,7 +1406,7 @@ A research agenda. *Politics and Governance*, *8*(2), 180–183.
-143\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition +151\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition formation in policy debates*. arXiv Preprint. @@ -1343,7 +1414,7 @@ formation in policy debates*. arXiv Preprint.
-144\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and +152\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and the conflict over software patents in Europe. *European Journal of Political Research*, *51*(3), 382–409. @@ -1352,7 +1423,7 @@ Political Research*, *51*(3), 382–409.
-145\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. +153\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. (2021). Belief system alignment and cross-sectoral advocacy efforts in policy debates. *Journal of European Public Policy*, 1–24. @@ -1361,7 +1432,7 @@ policy debates. *Journal of European Public Policy*, 1–24.
-146\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss +154\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss von Diskurskoalitionen auf Vermögensbesteuerung in Deutschland, 1995–2015: Eine Diskursnetzwerkanalyse von Policy-Wandel in der Steuerpolitik* \[Master's Thesis, FernUniversitĂ€t Hagen, FakultĂ€t fĂŒr @@ -1370,9 +1441,19 @@ Kultur- und Sozialwissenschaften, Institut fĂŒr Politikwissenschaft\].
+
+ +155\. Leipold, A. (2024). *Der Einfluss von Diskurskoalitionen auf die +Besteuerung von Unternehmen. Ein Vergleich von Pressedebatten und +Anhörungen im Deutschen Bundestag zur Steuergesetzgebung seit 1965* +\[Doctoral Dissertation, Leuphana UniversitĂ€t LĂŒneburg, FakultĂ€t +Staatswissenschaften\]. + +
+
-147\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in +156\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in nascent policy subsystems: Issue and instrument priorities across venues. *Policy Sciences*, *56*, 633–655. @@ -1381,7 +1462,7 @@ venues. *Policy Sciences*, *56*, 633–655.
-148\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., +157\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., Lagabrielle, E., Seen, D. L., & Degenne, P. (2017). Collaborative landscape research in Reunion Island: Using spatial modelling and simulation to support territorial foresight and urban planning. *Applied @@ -1391,7 +1472,7 @@ Geography*, *78*, 66–77.
-149\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s +158\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s family planning: Perspectives of advocacy coalitions. *International Journal of Environmental Research and Public Health*, *20*(6), 5204. @@ -1400,7 +1481,7 @@ Journal of Environmental Research and Public Health*, *20*(6), 5204.
-150\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, +159\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, M. (2023). Slovak MPs’ response to the 2022 Russian invasion of Ukraine in light of conspiracy theories and the polarization of political discourse. *Humanities and Social Sciences Communications*, *10*(1), @@ -1410,7 +1491,7 @@ discourse. *Humanities and Social Sciences Communications*, *10*(1),
-151\. Ličková, V. (2023). *Coal framing in the Indian political +160\. Ličková, V. (2023). *Coal framing in the Indian political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1418,7 +1499,7 @@ Studies\].
-152\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in +161\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in the acceleration phase of the European energy transition. *Environmental Innovation and Societal Transitions*, *40*, 262–282. @@ -1427,7 +1508,7 @@ Innovation and Societal Transitions*, *40*, 262–282.
-153\. Lockhart, C. (2014). *Discourse network analysis of the Northern +162\. Lockhart, C. (2014). *Discourse network analysis of the Northern Gateway Pipeline project: Assessing environmental governance in the joint review panel process* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental @@ -1437,7 +1518,7 @@ Governance Section\].
-154\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & +163\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & Wagner, P. M. (2021). Unity in diversity? When advocacy coalitions and policy beliefs grow trees in South Africa. *Land Use Policy*, *102*, 105283. @@ -1446,7 +1527,7 @@ policy beliefs grow trees in South Africa. *Land Use Policy*, *102*,
-155\. MalkamÀki, A. (2019). *On the human impacts and governance of +164\. MalkamÀki, A. (2019). *On the human impacts and governance of large-scale tree plantations* \[Doctoral Dissertation, University of Helsinki, Faculty of Agriculture; Forestry\]. @@ -1455,7 +1536,7 @@ Helsinki, Faculty of Agriculture; Forestry\].
-156\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & +165\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & YlÀ-Anttila, T. (2023). *Complex coalitions: Political alliances across relational contexts*. arXiv:2308.14422. @@ -1464,7 +1545,7 @@ relational contexts*. arXiv:2308.14422.
-157\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & +166\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & YlĂ€-Anttila, T. (2021). On the acoustics of policy learning: Can co-participation in policy forums break up echo chambers? *Policy Studies Journal*, *49*(2), 431–456. @@ -1473,7 +1554,7 @@ Studies Journal*, *49*(2), 431–456.
-158\. Mardiah, A. N. R. (2018). *Interface between disaster and +167\. Mardiah, A. N. R. (2018). *Interface between disaster and development: Local economic revival through collaborative post-disaster recovery governance and network in Indonesia* \[PhD thesis, University of Leeds, School of Geography\]. @@ -1483,7 +1564,7 @@ of Leeds, School of Geography\].
-159\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward +168\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward integrated and inclusive disaster risk reduction in Indonesia: Review of regulatory frameworks and institutional networks. In R. Djalante, M. Garschagen, F. Thomalla, & R. Shaw (Eds.), *Disaster risk reduction in @@ -1494,7 +1575,7 @@ Indonesia* (pp. 57–84). Springer.
-160\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. +169\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. A., & Hodgson, D. J. (2023). Stakeholder discourse coalitions and polarisation in the hen harrier conservation debate in news media. *People and Nature*, *5*(2), 668–683. @@ -1504,7 +1585,7 @@ polarisation in the hen harrier conservation debate in news media.
-161\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the +170\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the networking structuration processes of the urban resilience concept in Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1), 46–57. @@ -1513,18 +1594,26 @@ Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1),
-162\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing +171\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing transitions through the lens of discourse networks: Coal phase-out in Germany. *Environmental Innovation and Societal Transitions*, *40*, 315–331.
+
+ +172\. Mastroianni, L. (2024). How do crises affect policy subsystems? +The evolution of policy core beliefs in the EU asylum policy. *JCMS: +Journal of Common Market Studies*. + +
+
-163\. Mayer, C. D. (2022). *New west tension and threatened species +173\. Mayer, C. D. (2022). *New west tension and threatened species protection: The western Joshua tree conservation debate in the Morongo -Basin, california* \[Master's Thesis, California State University, Long +Basin, California* \[Master's Thesis, California State University, Long Beach, Department of Geography\]. @@ -1532,7 +1621,7 @@ Beach, Department of Geography\].
-164\. McDonald, E. (2019). *Energy security in the age of +174\. McDonald, E. (2019). *Energy security in the age of interconnection: Cyber-threat framing in British political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1541,7 +1630,7 @@ interconnection: Cyber-threat framing in British political discourse*
-165\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of +175\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of floating signifier: Discourses and network analysis in the bioeconomy policy processes in Argentina and Uruguay. *Forest Policy and Economics*, *154*, 103039. @@ -1551,7 +1640,7 @@ Economics*, *154*, 103039.
-166\. Miles, A. (2020). *Changes in social networks and narratives +176\. Miles, A. (2020). *Changes in social networks and narratives associated with Lake Erie water quality management after the 2014 Toledo water crisis* \[Master's Thesis, The Ohio State University, Graduate Program in Environment; Natural Resources\]. @@ -1561,7 +1650,7 @@ Program in Environment; Natural Resources\].
-167\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear +177\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear energy in the European Union and its implications* \[Doctoral Dissertation, Masaryk University, Department of International Relations; European Studies\]. @@ -1570,7 +1659,7 @@ European Studies\].
-168\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes +178\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes diffuse in space – explaining a missed transition in San Diego’s water sector. *Environmental Innovation and Societal Transitions*, *44*, 29–47. @@ -1579,7 +1668,7 @@ sector. *Environmental Innovation and Societal Transitions*, *44*,
-169\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, +179\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, renewable energy, or both? Public opinion on small hydropower plants in Serbia. *Post-Communist Economies*, *34*(5), 684–713. @@ -1588,7 +1677,7 @@ Serbia. *Post-Communist Economies*, *34*(5), 684–713.
-170\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). +180\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). Relational coupling of multiple streams: The case of COVID-19 infections in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374. @@ -1597,7 +1686,7 @@ in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374.
-171\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? +181\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? For whom? Intersectionality in Canada’s feminist international assistance policy. *International Journal*, *75*(3), 329–348. @@ -1606,7 +1695,7 @@ assistance policy. *International Journal*, *75*(3), 329–348.
-172\. Muller, A. (2014). Het meten van discourscoalities met +182\. Muller, A. (2014). Het meten van discourscoalities met discoursnetwerkanalyse: Naar een formele analyse van het politieke vertoog. *Res Publica*, *56*(3), 337–364. @@ -1615,7 +1704,7 @@ vertoog. *Res Publica*, *56*(3), 337–364.
-173\. Muller, A. (2015). Using discourse network analysis to measure +183\. Muller, A. (2015). Using discourse network analysis to measure discourse coalitions: Towards a formal analysis of political discourse. *World Political Science*, *11*(2), 377–404. @@ -1624,7 +1713,7 @@ discourse coalitions: Towards a formal analysis of political discourse.
-174\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big +184\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big tobacco? Discourse network analysis of the cigarette advertising debate and policy in Indonesia. *Journal of Communication Inquiry*. @@ -1633,7 +1722,7 @@ and policy in Indonesia. *Journal of Communication Inquiry*.
-175\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine +185\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS. @@ -1641,7 +1730,7 @@ Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS.
-176\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den +186\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 153–177). transcript Verlag. @@ -1651,7 +1740,7 @@ politische Konzepte* (pp. 153–177). transcript Verlag.
-177\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere +187\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 77–98). transcript @@ -1661,7 +1750,7 @@ Verlag.
-178\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level +188\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level governance dynamics from a discourse network perspective: The debate over air pollution regulation in Germany. *Environmental Sciences Europe*, *34*(62), 1–18. @@ -1670,7 +1759,7 @@ Europe*, *34*(62), 1–18.
-179\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A +189\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A discourse network analysis of the evolution of the conflict over Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700. @@ -1679,7 +1768,7 @@ Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700.
-180\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate +190\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate action: Comparing the evolution of narratives using the “narrative rate” index. *Review of Policy Research*, *40*(6), 1093–1119. @@ -1688,7 +1777,7 @@ index. *Review of Policy Research*, *40*(6), 1093–1119.
-181\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die +191\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS. @@ -1696,7 +1785,7 @@ Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS.
-182\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in +192\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in South Korea’s nuclear power policy making: An advocacy coalition framework approach to policy knowledge. *Politics & Policy*, *51*(2), 201–221. @@ -1705,7 +1794,7 @@ framework approach to policy knowledge. *Politics & Policy*, *51*(2),
-183\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames +193\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames of advocacy coalitions in South Korea’s nuclear energy policy. *Review of Policy Research*, *39*(4), 387–410. @@ -1714,7 +1803,7 @@ of Policy Research*, *39*(4), 387–410.
-184\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position +194\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position und Einfluss der Kirchen in der deutschen Debatte um die embryonale Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.), *Religionspolitik und Politik der Religionen in Deutschland* (pp. @@ -1724,7 +1813,7 @@ Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.),
-185\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting +195\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting in emerging economies – a systematic literature review and research agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820. @@ -1733,7 +1822,7 @@ agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820.
-186\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ +196\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ opozice vƯči hlubinnĂ©mu ĂșloĆŸiĆĄti radioaktivnĂ­ch odpadĆŻ v ČeskĂ© republice* \[PhD thesis, Masarykova univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\]. @@ -1742,7 +1831,7 @@ univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\].
-187\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech +197\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech newspaper coverage: A one man show. In D. Tindall, M. C. J. Stoddart, & R. E. Dunlap (Eds.), *Handbook of anti-environmentalism* (pp. 84–106). Edward Elgar Publishing. @@ -1751,7 +1840,7 @@ Edward Elgar Publishing.
-188\. Ohlendorf, N. (2022). *The political economy of energy +198\. Ohlendorf, N. (2022). *The political economy of energy transitions* \[Doctoral Thesis, Technical University of Berlin, FakultĂ€t VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\]. @@ -1760,7 +1849,7 @@ VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\].
-189\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in +199\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in multi-sector transitions – discourse analysis on hydrogen in Germany. *Environmental Innovation and Societal Transitions*, *47*, 100692. @@ -1769,7 +1858,7 @@ multi-sector transitions – discourse analysis on hydrogen in Germany.
-190\. Ohno, T. (2022). Advocacy coalition framework in environmental +200\. Ohno, T. (2022). Advocacy coalition framework in environmental governance studies: Explaining major policy change for a large dam removal in Japan. *International Review of Public Policy*, *4*(1). @@ -1778,7 +1867,7 @@ removal in Japan. *International Review of Public Policy*, *4*(1).
-191\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa +201\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa na polĂ­tica de transporte marĂ­timo de cargas no Brasil: Privatização, descentralização e abertura para o capital estrangeiro. *Caderno CRH – Revista de CiĂȘncias Sociais Do Centro de Estudos Pesquisas e Humanidades @@ -1789,7 +1878,7 @@ Da Universidade Federal Da Bahia*, *36*(2), 1–21.
-192\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of +202\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of oil and gas governance in Ghana* \[PhD thesis, University of Colorado Denver, School of Public Affairs\]. @@ -1798,16 +1887,16 @@ Denver, School of Public Affairs\].
-193\. Osei-Kojo, A. (2023). Analysing the stability of advocacy +203\. Osei-Kojo, A. (2023). Analysing the stability of advocacy coalitions and policy frames in Ghana’s oil and gas governance. *Policy -& Politics*, *51*(1), 71--90. +& Politics*, *51*(1), 71–90.
-194\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, +204\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, B. (2018). Natural gas market integration in the Visegrad 4 region: An example to follow or to avoid? *Energy Policy*, *112*, 184–197. @@ -1816,7 +1905,7 @@ example to follow or to avoid? *Energy Policy*, *112*, 184–197.
-195\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & +205\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & Kuhn, J. (2019). Who sides with whom? Towards computational construction of discourse networks for political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics*, @@ -1826,7 +1915,7 @@ Annual Meeting of the Association for Computational Linguistics*,
-196\. Palladino, N. (2021). The role of epistemic communities in the +206\. Palladino, N. (2021). The role of epistemic communities in the “constitutionalization” of internet governance: The example of the European Commission high-level expert group on artificial intelligence. *Telecommunications Policy*, *45*(6), 102149. @@ -1836,7 +1925,7 @@ European Commission high-level expert group on artificial intelligence.
-197\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, +207\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, P. (2020). Interactions among sustainable development goals: Knowledge for identifying multipliers and virtuous cycles. *Sustainable Development*, *28*(5), 1236–1250. @@ -1845,7 +1934,7 @@ Development*, *28*(5), 1236–1250.
-198\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des +208\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des Ă©chelles de la sĂ©curitĂ© dans une rĂ©gion Ă  gĂ©omĂ©trie variable* \[PhD thesis, UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\]. @@ -1855,7 +1944,7 @@ UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\].
-199\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and +209\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and climate change. A complex reflexive systems approach to energy transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.), *The routledge handbook of ideology and international relations* (pp. @@ -1863,18 +1952,37 @@ transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.),
+
+ +210\. PlĂŒmer, S. (2024). *Kongruentes Policy-Lernen als lernbedingter +Policy-Wandel. Zum Koordinationsmechanismus des Policy-Lernens in +Regierungsformationen*. Springer VS. + + +
+ +
+ +211\. A., P. I., Alekseev, D. V., & Zaripov, N. A. (2024). “Not by deed, +but by word”: A discourse network analysis of parliamentary discussions +of the bill on raising the retirement age in Russia. *Monitoring of +Public Opinion: Economic and Social Changes*, *2*, 116–140. + + +
+
-200\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing +212\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing psychedelics: The segregation of spiritual and scientific narratives within the r/microdosing online community. *Journal of Psychedelic -Studies*, *7*(2), 119–128. +Studies*, *7*(2), 119–128.
-201\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse +213\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse network analysis of French hydrogen politics* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1882,7 +1990,7 @@ University, Faculty of Social Studies\].
-202\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking +214\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking analysis as alternative research method in communication science studies – discourse networking analysis sebagai metode penelitian alternatif dalam kajian ilmu komunikasi. *Jurnal Penelitian Komunikasi Dan Opini @@ -1892,7 +2000,7 @@ Publik*, *21*(2), 126–136.
-203\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). +215\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). Discourse network analysis pada stakeholder dan integrated value creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*, *7*(2), 256–274. @@ -1901,7 +2009,7 @@ creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*,
-204\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., +216\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., Espressivo, A., Herlinda, O., Meilissa, Y., & Saminarsih, D. S. (2023). The advocacy coalition of sugar-sweetened beverage taxes in Indonesia. *BMJ Global Health*, *8*(Suppl 8), 1–13. @@ -1911,7 +2019,7 @@ The advocacy coalition of sugar-sweetened beverage taxes in Indonesia.
-205\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder +217\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder environmental governance in action: REDD+ discourse coalitions in Tanzania. *Ecology and Society*, *19*(2), 66–76. @@ -1920,7 +2028,7 @@ Tanzania. *Ecology and Society*, *19*(2), 66–76.
-206\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education +218\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education policy discourse: Philanthropic funders as entrepreneurs in policy networks. *Interest Groups & Advocacy*, *7*(3), 258–288. @@ -1929,7 +2037,7 @@ networks. *Interest Groups & Advocacy*, *7*(3), 258–288.
-207\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the +219\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the political economy of knowledge production shapes education policy: The case of teacher evaluation in federal policy discourse. *Educational Evaluation and Policy Analysis*, *43*(3), 472–494. @@ -1939,7 +2047,7 @@ Evaluation and Policy Analysis*, *43*(3), 472–494.
-208\. Rennkamp, B. (2019). Power, coalitions and institutional change in +220\. Rennkamp, B. (2019). Power, coalitions and institutional change in South African climate policy. *Climate Policy*, *19*(6), 756–770. @@ -1947,7 +2055,7 @@ South African climate policy. *Climate Policy*, *19*(6), 756–770.
-209\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. +221\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. (2017). Competing coalitions: The politics of renewable energy and fossil fuels in Mexico, South Africa and Thailand. *Energy Research & Social Science*, *34*, 214–223. @@ -1957,7 +2065,7 @@ Social Science*, *34*, 214–223.
-210\. Rinscheid, A. (2018). *Behavioral and institutionalist +222\. Rinscheid, A. (2018). *Behavioral and institutionalist perspectives on preference formation in a contested political context: The case of divesting from nuclear power* \[Doctoral Dissertation, University of St. Gallen, School of Management, Economics, Law, Social @@ -1968,7 +2076,7 @@ Sciences; International Affairs\].
-211\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy +223\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy change: Exploring the role of subsystem polarization in nuclear energy policymaking. *European Policy Analysis*, *1*(2), 34–70. @@ -1977,7 +2085,7 @@ policymaking. *European Policy Analysis*, *1*(2), 34–70.
-212\. Rinscheid, A. (2020). Business power in noisy politics: An +224\. Rinscheid, A. (2020). Business power in noisy politics: An exploration based on discourse network analysis and survey data. *Politics and Governance*, *8*(2), 286–297. @@ -1986,7 +2094,7 @@ exploration based on discourse network analysis and survey data.
-213\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. +225\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. (2020). Why do junctures become critical? Political discourse, agency, and joint belief shifts in comparative perspective. *Regulation & Governance*, *14*(4), 653–673. @@ -1995,7 +2103,7 @@ Governance*, *14*(4), 653–673.
-214\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The +226\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The diffusion of protest against the anti-counterfeiting trade agreement in the age of austerity* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2005,7 +2113,7 @@ Department of Political; Social Sciences\].
-215\. Rone, J. (2023). Instrumentalising sovereignty claims in British +227\. Rone, J. (2023). Instrumentalising sovereignty claims in British pro- and anti-Brexit mobilisations. *The British Journal of Politics and International Relations*, *25*(3), 444–461. @@ -2014,7 +2122,7 @@ International Relations*, *25*(3), 444–461.
-216\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff +228\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff increase policy in discourse analysis network. *Jurnal Komunikasi*, *17*(1), 62–75. @@ -2022,16 +2130,26 @@ increase policy in discourse analysis network. *Jurnal Komunikasi*,
-217\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, +229\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, right now: Storylines and social identities in coalition building in a local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247.
+
+ +230\. Samatan, N., Nurhasanah, N., Londol, M., Robingah, R., & Fania, S. +(2024). Critical discourse network analysis on Ferdy Sambo news in +online media using agenda setting theory. *Jurnal Komunikasi Ikatan +Sarjana Komunikasi Indonesia*, *9*(1), 146–167. + + +
+
-218\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, +231\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, R. R. (2023). Bias media mengenai pemberitaan batalnya Indonesia menjadi tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co. *Jurnal Ilmiah Wahana Pendidikan*, *9*(23), 547–556. @@ -2041,7 +2159,7 @@ tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co.
-219\. Schaub, S. (2021). *The politics of water protection* \[Doctoral +232\. Schaub, S. (2021). *The politics of water protection* \[Doctoral Dissertation, University of Heidelberg, Faculty of Economics; Social Studies, Institute of Political Science\]. @@ -2050,7 +2168,7 @@ Studies, Institute of Political Science\].
-220\. Schaub, S. (2021). Public contestation over agricultural +233\. Schaub, S. (2021). Public contestation over agricultural pollution: A discourse network analysis on narrative strategies in the policy process. *Policy Sciences*, *54*(4), 783–821. @@ -2059,7 +2177,7 @@ policy process. *Policy Sciences*, *54*(4), 783–821.
-221\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable +234\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable pharmacy? The influence of public debates on policy responses to pharmaceutical contaminants in water. *Environmental Sciences Europe*, *32*(1), 1–19. @@ -2068,7 +2186,7 @@ pharmaceutical contaminants in water. *Environmental Sciences Europe*,
-222\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy +235\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy network approaches: Evidence from water policy on micropollutants. *Politics and Governance*, *8*(2), 184–199. @@ -2077,7 +2195,7 @@ network approaches: Evidence from water policy on micropollutants.
-223\. Schmid, N. (2020). *The politics of technological change – case +236\. Schmid, N. (2020). *The politics of technological change – case studies from the energy sector* \[Doctoral Thesis, ETH ZĂŒrich, Department of Humanities, Social; Political Sciences, Energy; Technology Policy Group\]. @@ -2086,7 +2204,7 @@ Policy Group\].
-224\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining +237\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining advocacy coalition change with policy feedback. *Policy Studies Journal*, *48*(4), 1109–1134. @@ -2094,7 +2212,7 @@ Journal*, *48*(4), 1109–1134.
-225\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A +238\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A discourse network analysis of German parliamentary debates* \[Diploma Thesis, Charles University, Department of Security Studies\]. @@ -2103,7 +2221,7 @@ Thesis, Charles University, Department of Security Studies\].
-226\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, +239\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, partisanship and paradigmatic change in energy policy – analyzing parliamentary discourse in Germany over 30 years. *Climate Policy*, *19*(6), 771–786. @@ -2112,7 +2230,7 @@ parliamentary discourse in Germany over 30 years. *Climate Policy*,
-227\. Schmitz, L. (2018). *From coherence to coheritization. Explaining +240\. Schmitz, L. (2018). *From coherence to coheritization. Explaining the rise of policy coherence in EU external policy* \[Master's Thesis, Radboud University Nijmegen, Faculteit der Managementwetenschappen\]. @@ -2121,7 +2239,7 @@ Radboud University Nijmegen, Faculteit der Managementwetenschappen\].
-228\. Schmitz, L., & Eimer, T. R. (2020). From coherence to +241\. Schmitz, L., & Eimer, T. R. (2020). From coherence to coheritization: Explaining the rise of policy coherence in EU external policy. *Globalizations*, *17*(4), 629–647. @@ -2130,7 +2248,7 @@ policy. *Globalizations*, *17*(4), 629–647.
-229\. Schmitz, L., & Seidl, T. (2022). As open as possible, as +242\. Schmitz, L., & Seidl, T. (2022). As open as possible, as autonomous as necessary: Understanding the rise of open strategic autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*, *61*(3), 834–852. @@ -2139,7 +2257,7 @@ autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*,
-230\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of +243\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of technology legitimacy: Contending storylines surrounding wind energy in Austria and Switzerland. *Technological Forecasting and Social Change*, *198*, 122929. @@ -2148,7 +2266,7 @@ Austria and Switzerland. *Technological Forecasting and Social Change*,
-231\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and +244\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and displacements in policy discourse: The climate change issue in Germany 2007–2010. In S. Silvern & S. Young (Eds.), *Environmental change and sustainability* (pp. 157–184). Intech. @@ -2157,7 +2275,7 @@ sustainability* (pp. 157–184). Intech.
-232\. Schulz, C. (2020). Forest conservation through markets? A +245\. Schulz, C. (2020). Forest conservation through markets? A discourse network analysis of the debate on funding mechanisms for REDD+ in Brazil. *Environmental Communication*, *14*(2), 202–218. @@ -2166,16 +2284,25 @@ in Brazil. *Environmental Communication*, *14*(2), 202–218.
-233\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of +246\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of Twitter and newspapers: Lessons learned from the nuclear debate in the 2022 French presidential campaign. *French Politics*, *21*(2), 195–221.
+
+ +247\. Sconfienza, U. M., & Durand, F. (2024). Contested nuclear +discourses during the 2022 French presidential campaign. *Journal of +Environmental Policy & Planning*. + + +
+
-234\. Seidl, T. (2021). *Ideas, politics, and technological change: +248\. Seidl, T. (2021). *Ideas, politics, and technological change: Essays on the comparative political economy of digital capitalism* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2184,7 +2311,7 @@ Sciences\].
-235\. Seidl, T. (2022). The politics of platform capitalism: A case +249\. Seidl, T. (2022). The politics of platform capitalism: A case study on the regulation of Uber in New York. *Regulation & Governance*, *16*(2), 357–374. @@ -2192,7 +2319,7 @@ study on the regulation of Uber in New York. *Regulation & Governance*,
-236\. Selle, L. (2017). What multi-level parliamentary system? +250\. Selle, L. (2017). What multi-level parliamentary system? Parliamentary discourses in EU budgetary negotiations (MFF 2014–2020). In S. Becker, M. W. Bauer, & A. De Feo (Eds.), *The new politics of the European Union budget* (pp. 149–172). Nomos. @@ -2202,7 +2329,7 @@ European Union budget* (pp. 149–172). Nomos.
-237\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z +251\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2210,7 +2337,7 @@ University, Faculty of Social Studies\].
-238\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris +252\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris India: Unpacking consensus and conflict through storylines and discourse coalitions. *Energy Research & Social Science*, *91*, 102687. @@ -2219,7 +2346,7 @@ coalitions. *Energy Research & Social Science*, *91*, 102687.
-239\. Shukla, R., & Swarnakar, P. (2022). Energy transition and +253\. Shukla, R., & Swarnakar, P. (2022). Energy transition and dialectics: Tracing discursive resistance to coal through discourse coalition in India. *Globalizations*, *20*(8), 1296–1311. @@ -2228,7 +2355,7 @@ coalition in India. *Globalizations*, *20*(8), 1296–1311.
-240\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi +254\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi virus corona dengan discourse network analysis. *Jurnal Kebijakan Kesehatan Indonesia: JKKI*, *9*(2), 98–106. @@ -2237,7 +2364,7 @@ Kesehatan Indonesia: JKKI*, *9*(2), 98–106.
-241\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying +255\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying influence on EU efforts to regulate CO2 emissions of cars using network analysis. *Interest Groups & Advocacy*, *12*, 388–412. @@ -2246,7 +2373,7 @@ analysis. *Interest Groups & Advocacy*, *12*, 388–412.
-242\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan +256\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 34–49. @@ -2254,7 +2381,7 @@ UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2),
-243\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada +257\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina. *Kommunikatif*, *12*(1), 37–50. @@ -2262,7 +2389,7 @@ kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina.
-244\. Sohn, C. (2023). The impact of rebordering on cross-border +258\. Sohn, C. (2023). The impact of rebordering on cross-border cooperation actors’ discourses in the Öresund region. A semantic network approach. *Geografiska Annaler: Series B, Human Geography*. @@ -2271,7 +2398,7 @@ approach. *Geografiska Annaler: Series B, Human Geography*.
-245\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu +259\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*, *7*(2), 130–145. @@ -2279,7 +2406,7 @@ ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*,
-246\. Stancioff, C. E. (2016). Locality and landscape change: Cultural +260\. Stancioff, C. E. (2016). Locality and landscape change: Cultural values and social-ecological resiliency in the Kalinago territory. In T. Collins, G. Kindermann, C. Newman, & N. Cronin (Eds.), *Landscape values: Place and praxis. Conference, galway, 29th june–2nd july, 2016* @@ -2290,7 +2417,7 @@ values: Place and praxis. Conference, galway, 29th june–2nd july, 2016*
-247\. Starke, J. (2016). *Generating policy change in situations of +261\. Starke, J. (2016). *Generating policy change in situations of equilibrium: Shifting discourse networks in the case of wild circus animals in Germany* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance @@ -2300,7 +2427,7 @@ Section\].
-248\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. +262\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. J., & Termeer, K. J. A. M. (2023). “Green future” versus “planetary boundaries”? Evolving online discourse coalitions in European bioeconomy conflicts. *Journal of Cleaner Production*, *425*, 139058. @@ -2310,7 +2437,7 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
-249\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +263\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2318,7 +2445,7 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-250\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +264\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\]. @@ -2327,7 +2454,7 @@ of Governance, Master of Public Policy\].
-251\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +265\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2336,7 +2463,7 @@ co-exist?* Palgrave Macmillan.
-252\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +266\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2346,7 +2473,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-253\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +267\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2356,7 +2483,7 @@ alignments through discourse networks and intersectoral relationships.
-254\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +268\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2365,7 +2492,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-255\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +269\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2374,7 +2501,7 @@ Sociologie*, *53*(3), 316–336.
-256\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +270\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2383,7 +2510,7 @@ and the cultural dynamics of multilevel climate governance.
-257\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +271\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2393,7 +2520,7 @@ climate change: Canadian national news and mediated policy networks.
-258\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +272\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy Research*, *40*(6), 1144–1168. @@ -2402,7 +2529,7 @@ Research*, *40*(6), 1144–1168.
-259\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +273\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2411,7 +2538,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-260\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +274\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2420,7 +2547,7 @@ Economics and Environment*, *83*(4), 62–75.
-261\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +275\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2429,7 +2556,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-262\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +276\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2439,7 +2566,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-263\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +277\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2447,7 +2574,7 @@ thesis, Utrecht University\].
-264\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +278\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2456,7 +2583,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-265\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +279\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2465,7 +2592,7 @@ International Conference on Business and Public Administration (AICoBPA
-266\. Taranger, K. K. (2020). *The institutionalisation of climate +280\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2474,7 +2601,7 @@ Environmental Governance Section\].
-267\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +281\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2483,7 +2610,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-268\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +282\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2493,18 +2620,18 @@ Glasgow, School of Social; Political Sciences\].
-269\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +283\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates -on hydraulic fracturing. Comparing coalition politics in north america -and europe* (pp. 177–200). Palgrave Macmillan. +on hydraulic fracturing. Comparing coalition politics in North America +and Europe* (pp. 177–200). Palgrave Macmillan.
-270\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +284\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2513,8 +2640,8 @@ of Policy Research*, *34*(3), 310–330.
-271\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej -tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu* +285\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2522,7 +2649,7 @@ tranzĂ­cii v kontexte energiewende –- analĂœza politickĂ©ho diskurzu*
-272\. Tuinenburg, J. (2019). *The effect of discourse networks on the +286\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2531,7 +2658,7 @@ Governance\].
-273\. Umansky Casapa, N. (2022). *Securitization and social media +287\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2540,15 +2667,23 @@ Dublin, School of Politics; International Relations\].
-274\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +288\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\].
+
+ +289\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level +rise in a polycentric system. Easier said than done*. Cambridge +University Press. + +
+
-275\. Vaughan, M. (2020). Talking about tax: The discursive distance +290\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2557,7 +2692,7 @@ Politics*, *17*(2), 114–129.
-276\. Vedres, B. (2022). Multivocality and robust action dynamics in +291\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2565,7 +2700,7 @@ political discourse. *Poetics*, *90*, 101576.
-277\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +292\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2574,8 +2709,8 @@ Environmental Change*, *63*, 102117.
-278\. Vogeler, C. S. (2022). The integration of environmental objectives -in the common agricultural policy—partisan politics in the European +293\. Vogeler, C. S. (2022). The integration of environmental objectives +in the common agricultural policy – partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2583,7 +2718,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-279\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +294\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2592,7 +2727,7 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-280\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +295\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343. @@ -2601,16 +2736,16 @@ the European Parliament. *European Policy Analysis*, *7*, 324–343.
-281\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of +296\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of the far right: How far-right actors become mainstream in public debates. -*Political Communication*, 1–20. +*Political Communication*, *41*(3), 353–372.
-282\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +297\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2621,7 +2756,7 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
-283\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of +298\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of failure on socio-technical system configurations* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -2630,7 +2765,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-284\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +299\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2639,7 +2774,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-285\. Wallaschek, S. (2019). The discursive appeal to solidarity and +300\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2647,7 +2782,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-286\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +301\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2657,7 +2792,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-287\. Wallaschek, S. (2020). Analyzing the European parliamentary +302\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2667,7 +2802,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-288\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +303\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2676,7 +2811,7 @@ European Public Policy*, *27*(7), 1034–1053.
-289\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +304\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2685,7 +2820,7 @@ Economy*, *25*(2), 231–247.
-290\. Wallaschek, S. (2020). The discursive construction of solidarity: +305\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2693,7 +2828,7 @@ Studies*, *68*(1), 74–92.
-291\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +306\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2701,7 +2836,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-292\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +307\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2711,7 +2846,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-293\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +308\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2720,7 +2855,7 @@ the public sphere: A discourse network analysis of German newspapers
-294\. Wang, S. (2018). *Dynamic constructed climate change discourses +309\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2728,9 +2863,17 @@ Politics\].
+
+ +310\. Wang, S. (2024). *Communicating climate change in China. A dynamic +discourse approach*. Palgrave Macmillan. + + +
+
-295\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +311\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2739,7 +2882,7 @@ Public Policy*, *44*(3), 354–368.
-296\. Wang, Y. (2021). Examining the actor coalitions and discourse +312\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2748,7 +2891,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-297\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +313\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2757,26 +2900,26 @@ Policy Analysis Archives*, *25*(34), 1–27.
-298\. Wang, Y. (2020). Understanding Congressional coalitions: A +314\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30.
-
+
-299\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & -Hekkert, M. P. (2023). The influence of visions on cooperation among +315\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +Hekkert, M. P. (2024). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. -*Environmental Policy and Governance*. +*Environmental Policy and Governance*, *34*(2), 152–165.
-300\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +316\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2785,7 +2928,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-301\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +317\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2793,9 +2936,19 @@ The influence of global and national institutional dynamics.
+
+ +318\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using +collaborative governance to regulate sober living facilities: Structures +and strategies for mitigating the influence of powerful actors in +multi-sectoral networks. *Administration & Society*, *56*(4), 473–510. + + +
+
-302\. Wu, J., & Liu, Y. (2020). Deception detection methods +319\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2804,24 +2957,25 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-303\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +320\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948.
-
+
-304\. Yan, K., Wu, H., Bu, K., & Wu, L. (2023). The college admission +321\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. -*Higher Education Policy*. +*Higher Education Policy*, *37*, 209–236. +
-305\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +322\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -2830,25 +2984,25 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-306\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +323\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32.
-
+
-307\. Yordy, J., Durnová, A., & Weible, C. M. (2023). Exploring +324\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring emotional discourses: The case of COVID-19 protests in the US media. -*Administrative Theory & Praxis*. +*Administrative Theory & Praxis*, *46*(1), 35–54.
-308\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +325\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -2857,7 +3011,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-309\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +326\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -2866,16 +3020,16 @@ emotional discourses: The case of COVID-19 protests in the US media.
-310\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +327\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), -107--129. +107–129.
-311\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +328\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 22068348..877fccd5 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -46,4 +46,4 @@ URL: https://github.com/leifeld/dna BugReports: https://github.com/leifeld/dna/issues Encoding: UTF-8 Language: en-US -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 From b3df500eda3665c85e3d8d749f5010b24a0c20cc Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Tue, 16 Jul 2024 16:06:13 +0200 Subject: [PATCH 14/43] Updated affiliations in DNA and rDNA --- dna/src/main/java/dna/Dna.java | 6 +++--- rDNA/rDNA/DESCRIPTION | 8 ++++---- rDNA/rDNA/R/rDNA.R | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 2b19ea3a..d132e8ea 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -11,13 +11,13 @@ /** * Main class of the Discourse Network Analyzer (DNA). * - * @author Philip Leifeld {@literal } + * @author Philip Leifeld {@literal } */ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-02-13"; + public static final String date = "2024-07-16"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; @@ -88,4 +88,4 @@ public void uncaughtException(Thread t, Throwable e) { logger.log(le); } } -} \ No newline at end of file +} diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 877fccd5..ecf45097 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,15 +1,15 @@ Package: rDNA Version: 3.0.11 -Date: 2024-02-10 +Date: 2024-07-16 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", family = "Leifeld", - email = "Philip.Leifeld@essex.ac.uk", + email = "philip.leifeld@manchester.ac.uk", role = c("aut", "cre", "cph")), person(given = "Tim", family = "Henrichsen", - email = "Tim.Henrichsen@warwick.ac.uk", + email = "t.henrichsen@bham.ac.uk", role = "ctb"), person(given = "Johannes B.", family = "Gruber", @@ -19,7 +19,7 @@ Authors@R: family = "Garic", email = "k.garic@essex.ac.uk", role = "ctb")) -Maintainer: Philip Leifeld +Maintainer: Philip Leifeld Description: Qualitative content analysis and discourse network analysis using the Java software Discourse Network Analyzer (DNA). Depends: diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index bf5d6848..a1ba7683 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -10,9 +10,9 @@ dnaEnvironment <- new.env(hash = TRUE, parent = emptyenv()) packageStartupMessage( 'Version: ', desc$Version, '\n', 'Date: ', desc$Date, '\n', - 'Author: Philip Leifeld (University of Essex)\n', - 'Contributors: Tim Henrichsen (University of Warwick),\n', - ' Johannes B. Gruber (University of Amsterdam)\n', + 'Author: Philip Leifeld (University of Manchester)\n', + 'Contributors: Tim Henrichsen (University of Birmingham),\n', + ' Johannes B. Gruber (Vrije Universiteit Amsterdam)\n', ' Kristijan Garic (University of Essex)\n', 'Project home: github.com/leifeld/dna' ) From c2f799aceb7d263bd3d03b0ed16aefe9a8f8a06e Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Tue, 16 Jul 2024 16:44:27 +0200 Subject: [PATCH 15/43] Added installation instructions --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c4017835..e241a87e 100755 --- a/README.md +++ b/README.md @@ -14,9 +14,31 @@ The Java software Discourse Network Analyzer (DNA) is a qualitative content anal [DNA 3.0](https://github.com/leifeld/dna/releases) was first released on 12 June 2022. It constitutes a major rewrite from the previous version DNA 2.0 beta 25. DNA 3 comes with many new features and improvements. The [release](https://github.com/leifeld/dna/releases) page contains all the details (scroll to version 3.0.7 for the first DNA 3 release). +[![DNA/rDNA build](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml/badge.svg)](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml) + +## Installation of DNA + +DNA was written in Java and requires Java 11 or higher on your system. You can check if you have Java >= 11 on your system by opening the terminal of your operating system (e.g., type `cmd` in your Windows start menu or use the terminal app on MacOS) and typing: + +``` shell +java --version +``` + +If this indicates a version below 11 (or 1.11), installing the latest version of [Adoptium Temurin OpenJDK](https://adoptium.net) is recommended before you proceed. Once installed, restart your system and check the version again. + +To use DNA, simply download the latest `.jar` file under "[Releases](https://github.com/leifeld/dna/releases)" on the right along with the `sample.dna` file, a toy database you can load in DNA (password: `sample`) to play with the software. You can double-click on the `.jar` file to open DNA. No installation is required. + +MacOS users may need to give the program authorization to be executed. Some MacOS users reported issues with opening files. These issues should have been fixed in version 3.0.11. You can also browse the [issue tracker](https://github.com/leifeld/issues) (including closed issues) and the [commit messages](https://github.com/leifeld/dna/commits/master/) for more information on MacOS issues. Get in touch via the issue tracker or Matrix (infos below) if you are unable to solve these issues. + +If your system does not want to start DNA with a double-click on the `.jar` file, you can also open it from the terminal. To do so, navigate to the directory where the `.jar` file is stored on your computer using the `cd` command, for example `cd ~/Documents/`. Once there, type in something like this, with `.jar` file corresponding to the file version you downloaded: + +``` shell +java -jar dna-3.0.10.jar +``` + If you require the latest (non-release) version of the DNA jar file from GitHub, you can clone the git repository to your computer and execute `./gradlew build` on your terminal or command line. This will build the jar file and store it in the `build/` directory of the cloned repository. Alternatively, you can try to download the latest artifact from the build process under [GitHub Actions](https://github.com/leifeld/dna/actions) by clicking on the latest build and scrolling down to "Artifacts". However, it is usually recommended to use the most recent [release](https://github.com/leifeld/dna/releases/) version. -[![DNA/rDNA build](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml/badge.svg)](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml) +Along with DNA, it is recommended to download [visone](https://visone.ethz.ch/), which can be opened in the same way as DNA. You can open `.graphml` files from DNA's network export in visone. ## rDNA 3.0: Connecting DNA to R From abdb9544270c3b25d03f0cdbd95d46677e489c8e Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Tue, 16 Jul 2024 17:22:07 +0200 Subject: [PATCH 16/43] Updated bibliography --- bibliography/bibliography.bib | 22 ++ build/bibliography.md | 640 +++++++++++++++++----------------- 2 files changed, 351 insertions(+), 311 deletions(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index 90380ecf..e806fad7 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -185,6 +185,28 @@ @article{baulenas2021she doi={10.1016/j.landusepol.2020.105182} } +@article{belanger2021communicating, + title={Communicating Mobility Restrictions during the {C}OVID-19 Crisis on {T}witter: The Legitimacy Challenge}, + author={B{\'e}langer, Marie-Eve and Lavenex, Sandra}, + journal={Swiss Political Science Review}, + volume={27}, + number={4}, + pages={822--839}, + year={2021}, + doi={10.1111/spsr.12494} +} + +@article{belanger2023justifying, + title={Justifying Mobility Restrictions during the {C}OVID-19 Pandemic: A Test in Multilevel Governance}, + author={B{\'e}langer, Marie-Eve and Lavenex, Sandra}, + journal={West European Politics}, + volume={46}, + number={7}, + pages={1343--1368}, + year={2023}, + doi={10.1080/01402382.2023.2197330} +} + @mastersthesis{belova2021another, title={Another Silver Bullet for the Energy Transition? {D}iscourse Network Analysis of {G}erman Hydrogen Debate}, author={Belova, Arina}, diff --git a/build/bibliography.md b/build/bibliography.md index 0baf2edf..d146f591 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -178,9 +178,27 @@ management integration in Germany, Spain and Sweden. *Land Use Policy*,
+
+ +18\. BĂ©langer, M.-E., & Lavenex, S. (2021). Communicating mobility +restrictions during the COVID-19 crisis on Twitter: The legitimacy +challenge. *Swiss Political Science Review*, *27*(4), 822–839. + + +
+ +
+ +19\. BĂ©langer, M.-E., & Lavenex, S. (2023). Justifying mobility +restrictions during the COVID-19 pandemic: A test in multilevel +governance. *West European Politics*, *46*(7), 1343–1368. + + +
+
-18\. Belova, A. (2021). *Another silver bullet for the energy +20\. Belova, A. (2021). *Another silver bullet for the energy transition? Discourse network analysis of German hydrogen debate* \[Master’s thesis, Department of International Relations; European Studies, Energy Policy Studies, Masaryk University\]. @@ -190,7 +208,7 @@ Studies, Energy Policy Studies, Masaryk University\].
-19\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & +21\. Belova, A., Quittkat, C., Lehotskỳ, L., Knodt, M., Osička, J., & Kemmerzell, J. (2023). The more the merrier? Actors and ideas in the evolution of German hydrogen policy discourse. *Energy Research & Social Science*, *97*, 102965. @@ -199,7 +217,7 @@ Science*, *97*, 102965.
-20\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse +22\. Bhattacharya, C. (2020). Gatekeeping the plenary floor: Discourse network analysis as a novel approach to party control. *Politics and Governance*, *8*(2), 229–242. @@ -207,7 +225,7 @@ Governance*, *8*(2), 229–242.
-21\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a +23\. Bhattacharya, C. (2023). Restrictive rules of speechmaking as a tool to maintain party unity: The case of oppressed political conflict in German parliament debates on the Euro crisis. *Party Politics*, *29*(3), 554–569. @@ -216,7 +234,7 @@ in German parliament debates on the Euro crisis. *Party Politics*,
-22\. Bisht, B. S. (2023). *Media discourse analysis: Solar power in +24\. Bisht, B. S. (2023). *Media discourse analysis: Solar power in India. The case of Gujarat* \[Master's Thesis, Masaryk University, Faculty of Social Studies, Department of International Relations; European Studies\]. @@ -225,7 +243,7 @@ European Studies\].
-23\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network +25\. BlaĆŸek, D. (2021). *Does the climate matter? Discourse network analysis of climate delayism in the Czech Republic* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. @@ -234,7 +252,7 @@ Masaryk University, Faculty of Social Studies\].
-24\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & +26\. Blessing, A., Blokker, N., Haunss, S., Kuhn, J., Lapesa, G., & Padó, S. (2019). An environment for relational annotation of political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics: System Demonstrations*, 105–110. @@ -244,7 +262,7 @@ Computational Linguistics: System Demonstrations*, 105–110.
-25\. Boas, I. (2015). *Climate migration and security: Securitisation as +27\. Boas, I. (2015). *Climate migration and security: Securitisation as a strategy in climate change politics*. Routledge. @@ -252,7 +270,7 @@ a strategy in climate change politics*. Routledge.
-26\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice +28\. BodiĆĄovĂĄ, Äœ. (2018). *EurĂłpske zĂĄujmovĂ© skupiny a revĂ­zia smernice o energetickej efektĂ­vnosti – analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -261,7 +279,7 @@ Thesis, Masaryk University, Faculty of Social Studies\].
-27\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual +29\. Bossner, F., & Nagel, M. (2020). Discourse networks and dual screening: Analyzing roles, content and motivations in political Twitter conversations. *Politics and Governance*, *8*(2), 311–325. @@ -270,7 +288,7 @@ conversations. *Politics and Governance*, *8*(2), 311–325.
-28\. Brandenberger, L. (2019). Predicting network events to assess +30\. Brandenberger, L. (2019). Predicting network events to assess goodness of fit of relational event models. *Political Analysis*, *27*(4), 556–571. @@ -278,7 +296,7 @@ goodness of fit of relational event models. *Political Analysis*,
-29\. Breindl, Y. (2013). Discourse networks on state-mandated access +31\. Breindl, Y. (2013). Discourse networks on state-mandated access blocking in Germany and France. *Info*, *15*(6), 42–62. @@ -286,7 +304,7 @@ blocking in Germany and France. *Info*, *15*(6), 42–62.
-30\. Bresser, B. (2024). *Transitions in the risk assessment regime: +32\. Bresser, B. (2024). *Transitions in the risk assessment regime: Prospects from the case of animal-free testing in the cosmetics industry* \[Master's Thesis, Utrecht University, Innovation Sciences\]. @@ -295,7 +313,7 @@ industry* \[Master's Thesis, Utrecht University, Innovation Sciences\].
-31\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, +33\. Broadbent, J., Sonnett, J., Botetzagias, I., Carson, M., Carvalho, A., Chien, Y.-J., Edling, C., Fisher, D. R., Giouzepas, G., Haluza-DeLay, R., Hazegawa, K., Hirschi, C., Horta, A., Ikeda, K., Jin, J., Ku, D., Lahsen, M., Lee, H.-C., Lin, T.-L. A., 
 Zhengyi, S. (2016). @@ -307,7 +325,7 @@ Conflicting climate change frames in a global field of media discourse.
-32\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis +34\. Broadbent, J., & Vaughter, P. (2014). Inter-disciplinary analysis of climate change and society: A network approach. In M. J. Manfredo, J. J. Vaske, A. Rechkemmer, & E. A. Duke (Eds.), *Understanding society and natural resources. Forging new strands of integration across the social @@ -318,7 +336,7 @@ sciences* (pp. 203–228). Springer Open.
-33\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: +35\. Brugger, F., & Engebretsen, R. (2022). Defenders of the status quo: Making sense of the international discourse on transfer pricing methodologies. *Review of International Political Economy*, *29*(1), 307–335. @@ -327,7 +345,7 @@ methodologies. *Review of International Political Economy*, *29*(1),
-34\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse +36\. Brugger, H., & Henry, A. D. (2021). Influence of policy discourse networks on local energy transitions. *Environmental Innovation and Societal Transitions*, *39*, 141–154. @@ -336,7 +354,7 @@ Societal Transitions*, *39*, 141–154.
-35\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die +37\. Brunner, L. (2021). *Der Einfluss von Interessensgruppen auf die US-Außenpolitik. Ein Vergleich der Israelpolitik zwischen den Kabinetten Obama II und Trump* \[Master's Thesis, Leopold-Franzens-UniversitĂ€t Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\]. @@ -346,7 +364,7 @@ Innsbruck, FakultĂ€t fĂŒr Soziale und Politische Wissenschaften\].
-36\. Brutschin, E. (2013). *Dynamics in EU policy-making: The +38\. Brutschin, E. (2013). *Dynamics in EU policy-making: The liberalization of the European gas market* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -355,7 +373,7 @@ University of Konstanz, Department of Politics; Public Administration\].
-37\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A +39\. Buckton, C. H., Fergie, G., Leifeld, P., & Hilton, S. (2019). A discourse network analysis of UK newspaper coverage of the “sugar tax” debate before and after the announcement of the Soft Drinks Industry Levy. *BMC Public Health*, *19*(490), 1–14. @@ -365,7 +383,7 @@ Levy. *BMC Public Health*, *19*(490), 1–14.
-38\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of +40\. Cassiers, T. (2018). *“Politics... Or just work”? On the role of territoriality in policy networks. The case of transportation policies in the cross-border metropolitan regions of Brussels and Luxembourg* \[Doctoral Dissertation, KU Leuven, Division of Geography; Tourism\]. @@ -375,7 +393,7 @@ in the cross-border metropolitan regions of Brussels and Luxembourg*
-39\. Černỳ, O. (2018). *Limity českĂ© +41\. Černỳ, O. (2018). *Limity českĂ© energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -384,7 +402,7 @@ energetickĂ© tranzice v politickĂ© perspektivě: Pƙípad tÄ›ĆŸby uhlĂ­*
-40\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media +42\. Černỳ, O., & Ocelı́k, P. (2020). Incumbents’ strategies in media coverage: A case of the Czech coal policy. *Politics and Governance*, *8*(2), 272–285. @@ -392,7 +410,7 @@ coverage: A case of the Czech coal policy. *Politics and Governance*,
-41\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej +43\. Cicko, M. (2018). *Analỳza diskurzĂ­vnych sietĂ­ v energetickej politike Českej republiky* \[Master's Thesis, Masarykova univerzita, Fakulta sociĂĄlnı́ch studiı́\]. @@ -400,7 +418,7 @@ Fakulta sociĂĄlnı́ch studiı́\].
-42\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green +44\. Ciordia Morandeira, A. (2020). *Less divided after ETA? Green networks in the Basque country between 2007 and 2017* \[PhD thesis, University of Trento, School of Social Sciences\]. @@ -409,7 +427,7 @@ University of Trento, School of Social Sciences\].
-43\. Clasing-Manquian, P. (2024). How free tuition cecame a policy in +45\. Clasing-Manquian, P. (2024). How free tuition cecame a policy in Chile: The importance of policy actors and their beliefs. *Higher Education*. @@ -417,7 +435,7 @@ Education*.
-44\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un +46\. Coronado Vigueras, R. A. (2015). *La reforma tributaria 2014: Un anĂĄlisis desde las coaliciones discursivas* \[Tesis Postgrado, Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\]. @@ -426,7 +444,7 @@ Universidad de Chile, Facultad de Ciencias FĂ­sicas y MatemĂĄticas\].
-45\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking +47\. Cross, J. P., Greene, D., Umansky, N., & CalĂČ, S. (2023). Speaking in unison? Explaining the role of agenda-setter constellations in the ECB policy agenda using a network-based approach. *Journal of European Public Policy*. @@ -435,7 +453,7 @@ Public Policy*.
-46\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). +48\. Dalimunthe, S. A., Putri, I. A. P., & Prasojo, A. P. S. (2022). Depicting mangrove’s potential as blue carbon champion in Indonesia. In R. Dasgupta, S. Hashimoto, & O. Saito (Eds.), *Assessing, mapping and modelling of mangrove ecosystem services in the Asia-Pacific region* @@ -445,7 +463,7 @@ modelling of mangrove ecosystem services in the Asia-Pacific region*
-47\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse +49\. Demler, K., & Thurm, S. (2023). Against all odds? A discourse network analysis of the political debate about the German passenger car toll act. *German Politics*. @@ -454,7 +472,7 @@ toll act. *German Politics*.
-48\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the +50\. Dı́az, A. O., & GutiĂ©rrez, E. C. (2018). Competing actors in the climate change arena in Mexico: A network analysis. *Journal of Environmental Management*, *215*, 239–247. @@ -463,7 +481,7 @@ Environmental Management*, *215*, 239–247.
-49\. Drozdzynski, F. A. (2022). *The Common Agricultural Policy post +51\. Drozdzynski, F. A. (2022). *The Common Agricultural Policy post 2020: An analysis of the beliefs of selected key stakeholders* \[Master's Thesis, University of Twente, Faculty of Behavioural, Management; Social Sciences\]. @@ -472,7 +490,7 @@ Management; Social Sciences\].
-50\. Durel, L. (2024). Border carbon adjustment compliance and the WTO: +52\. Durel, L. (2024). Border carbon adjustment compliance and the WTO: The interactional evolution of law. *Journal of International Economic Law*, *27*(1), 18–40. @@ -480,7 +498,7 @@ Law*, *27*(1), 18–40.
-51\. Duygan, M. (2018). *An actor-based analysis of political context +53\. Duygan, M. (2018). *An actor-based analysis of political context for supporting sustainability transitions of socio-technical systems: A study of Swiss waste management* \[Doctoral Dissertation, ETH ZĂŒrich, Department of Environmental Systems Science\]. @@ -490,7 +508,7 @@ Department of Environmental Systems Science\].
-52\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for +54\. Duygan, M., Stauffacher, M., & Meylan, G. (2019). A heuristic for conceptualizing and uncovering the determinants of agency in socio-technical transitions. *Environmental Innovation and Societal Transitions*, *33*, 13–29. @@ -499,7 +517,7 @@ Transitions*, *33*, 13–29.
-53\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse +55\. Duygan, M., Stauffacher, M., & Meylan, G. (2018). Discourse coalitions in Swiss waste management: Gridlock or winds of change? *Waste Management*, *72*, 25–44. @@ -508,7 +526,7 @@ coalitions in Swiss waste management: Gridlock or winds of change?
-54\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes +56\. Duygan, M., Stauffacher, M., & Meylan, G. (2021). What constitutes agency? Determinants of actors’ influence on formal institutions in Swiss waste management. *Technological Forecasting and Social Change*, *162*, 120413. @@ -517,7 +535,7 @@ Swiss waste management. *Technological Forecasting and Social Change*,
-55\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How +57\. Eberlein, B., & Rinscheid, A. (2020). Building bridges: How discourse network analysis (DNA) can help CSR research to investigate the “new” political role of corporations. In M. Nagel, P. Kenis, P. Leifeld, & H.-J. Schmedes (Eds.), *Politische KomplexitĂ€t, Governance @@ -528,14 +546,14 @@ von Innovationen und Policy-Netzwerke* (pp. 139–146). Springer VS.
-56\. Eder, F. (2015). *Der Irakkrieg 2003*. Innsbruck University Press. +58\. Eder, F. (2015). *Der Irakkrieg 2003*. Innsbruck University Press.
-57\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. +59\. Eder, F. (2023). Discourse network analysis. In P. A. Mello & F. Ostermann (Eds.), *Routledge Handbook of foreign policy analysis methods* (pp. 516–535). Taylor & Francis. @@ -544,7 +562,7 @@ methods* (pp. 516–535). Taylor & Francis.
-58\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, +60\. Eder, F. (2019). Making concurrence-seeking visible: Groupthink, discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*, *15*(1), 21–42. @@ -552,7 +570,7 @@ discourse networks, and the 2003 Iraq war. *Foreign Policy Analysis*,
-59\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting +61\. Eder, F., Libiseller, C., & Schneider, B. (2021). Contesting counter-terrorism: Discourse networks and the politicisation of counter-terrorism in Austria. *Journal of International Relations and Development*, *24*(1), 171–195. @@ -562,7 +580,7 @@ Development*, *24*(1), 171–195.
-60\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan +62\. Edvra, P. A., & Ahmad, N. (2023). Tipologi jaringan wacana dan komunikator publik dalam berita omicron baru di media online. *Jurnal Riset Komunikasi*, *6*(1), 58–79. @@ -571,7 +589,7 @@ Riset Komunikasi*, *6*(1), 58–79.
-61\. Elislah, N. (2023). Discourse network analysis on delaying +63\. Elislah, N. (2023). Discourse network analysis on delaying elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2), 225–240. @@ -579,7 +597,7 @@ elections in President Joko Widodo’s era. *Jurnal Aspikom*, *8*(2),
-62\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue +64\. Eriyanto, & Ali, D. J. (2020). Discourse network of a public issue debate: A study on Covid-19 cases in Indonesia. *Jurnal Komunikasi: Malaysian Journal of Communication*, *36*(3), 209–227. @@ -588,7 +606,7 @@ Malaysian Journal of Communication*, *36*(3), 209–227.
-63\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping +65\. Fergie, G., Leifeld, P., Hawkins, B., & Hilton, S. (2019). Mapping discourse coalitions in the minimum unit pricing for alcohol debate: A discourse network analysis of UK newspaper coverage. *Addiction*, *114*(4), 741–753. @@ -597,7 +615,7 @@ discourse network analysis of UK newspaper coverage. *Addiction*,
-64\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological +66\. Ferrare, J., Carter-Stone, L., & Galey-Horn, S. (2021). Ideological tensions in education policy networks: An analysis of the policy innovators in education network in the United States. *Foro de Educacion*, *19*(1), 11–28. @@ -606,7 +624,7 @@ Educacion*, *19*(1), 11–28.
-65\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban +67\. Filippini, R., Mazzocchi, C., & Corsi, S. (2015). Trends in urban food strategies. In C. Tornaghi (Ed.), *Re-imagining sustainable food planning, building resourcefulness: Food movements, insurgent planning and heterodox economics: Proceedings of the 8th annual conference AESOP @@ -617,7 +635,7 @@ sustainable food planning group* (pp. 79–88). Coventry University.
-66\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate +68\. Fisher, D. R., & Leifeld, P. (2019). The polycentricity of climate policy blockage. *Climatic Change*, *155*(4), 469–487. @@ -625,7 +643,7 @@ policy blockage. *Climatic Change*, *155*(4), 469–487.
-67\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the +69\. Fisher, D. R., Leifeld, P., & Iwaki, Y. (2013). Mapping the ideological networks of American climate politics. *Climatic Change*, *116*(3), 523–545. @@ -633,7 +651,7 @@ ideological networks of American climate politics. *Climatic Change*,
-68\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does +70\. Fisher, D. R., Waggle, J., & Leifeld, P. (2013). Where does political polarization come from? Locating polarization within the U.S. Climate change debate. *American Behavioral Scientist*, *57*(1), 70–92. @@ -642,7 +660,7 @@ Climate change debate. *American Behavioral Scientist*, *57*(1), 70–92.
-69\. Friis, G. (2020). *Populist radical right parties into parliament: +71\. Friis, G. (2020). *Populist radical right parties into parliament: Changes in mainstream parties’ political positions in parliamentary debates on immigration and refugees* \[Master’s thesis, Uppsala University, Disciplinary Domain of Humanities; Social Sciences, Faculty @@ -653,7 +671,7 @@ of Social Sciences, Department of Government\].
-70\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network +72\. Galey-Horn, S., & Ferrare, J. J. (2020). Using policy network analysis to understand ideological convergence and change in educational subsystems. *Education Policy Analysis Archives*, *28*(118). @@ -662,7 +680,7 @@ subsystems. *Education Policy Analysis Archives*, *28*(118).
-71\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). +73\. Galey-Horn, S., Reckhow, S., Ferrare, J. J., & Jasny, L. (2020). Building consensus: Idea brokerage in teacher policy networks. *American Educational Research Journal*, *57*(2), 872–905. @@ -671,7 +689,7 @@ Educational Research Journal*, *57*(2), 872–905.
-72\. Galli Robertson, A. M. (2021). Privileged accounts in the debate +74\. Galli Robertson, A. M. (2021). Privileged accounts in the debate over coal-fired power in the United States. *Society & Natural Resources*, *34*(2), 188–207. @@ -680,7 +698,7 @@ Resources*, *34*(2), 188–207.
-73\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable +75\. Gallmann, M. R. (2021). *Depicting climate change in a vulnerable country: Agenda-setting and a discourse network approach on Philippine broadsheet media* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -690,7 +708,7 @@ Science, Oeschger Centre for Climate Change Research\].
-74\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The +76\. Geddes, A., Schmid, N., Schmidt, T. S., & Steffen, B. (2020). The politics of climate finance: Consensus and partisanship in designing green state investment banks in the United Kingdom and Australia. *Energy Research & Social Science*, *69*, 101583. @@ -700,7 +718,7 @@ green state investment banks in the United Kingdom and Australia.
-75\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local +77\. Ghinoi, S., De Vita, R., & Silvestri, F. (2023). Local policymakers’ attitudes towards climate change: A multi-method case study. *Social Networks*, *25*, 197–209. @@ -709,7 +727,7 @@ study. *Social Networks*, *25*, 197–209.
-76\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates +78\. Ghinoi, S., Junior, V. J. W., & Piras, S. (2018). Political debates and agricultural policies: Discourse coalitions behind the creation of Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80. @@ -718,7 +736,7 @@ Brazil’s Pronaf. *Land Use Policy*, *76*, 68–80.
-77\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social +79\. Ghinoi, S., & Omori, M. (2023). Expert knowledge and social innovation: Analysing policy debates in Japan. *Journal of Social Entrepreneurship*. @@ -726,7 +744,7 @@ Entrepreneurship*.
-78\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate +80\. Ghinoi, S., & Steiner, B. (2020). The political debate on climate change in Italy: A discourse network analysis. *Politics and Governance*, *8*(2), 215–228. @@ -734,7 +752,7 @@ Governance*, *8*(2), 215–228.
-79\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and +81\. Gielens, E., Roosma, F., & Achterberg, P. (2023). Between left and right: A discourse network analysis of universal basic income on Dutch Twitter. *Journal of Social Policy*. @@ -743,7 +761,7 @@ Twitter. *Journal of Social Policy*.
-80\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage +82\. Gkiouzepas, G., & Botetzagias, I. (2017). Climate change coverage in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4), 490–514. @@ -751,7 +769,7 @@ in Greek newspapers: 2001–2008. *Environmental Communication*, *11*(4),
-81\. Goritz, A., & Kolleck, N. (2024). Education in international +83\. Goritz, A., & Kolleck, N. (2024). Education in international climate pledges – identifying education framings in countries nationally determined contributions (NDCs). *Environmental Education Research*. @@ -760,7 +778,7 @@ determined contributions (NDCs). *Environmental Education Research*.
-82\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of +84\. GrĂŒnwald, L. (2023). *Roadblocks of polarization: Mechanisms of cultural resistance to a speed limit on German highways* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -769,7 +787,7 @@ Development\].
-83\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, +85\. Gupta, K., Ripberger, J., Fox, A., Jenkins-Smith, H. C., & Silva, C. (2022). Discourse network analysis of nuclear narratives. In M. D. Jones, M. K. McBeth, & E. Shanahan (Eds.), *Narratives and the policy process: Applications of the narrative policy framework* (pp. 13–38). @@ -779,7 +797,7 @@ Montana State University Library.
-84\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations +86\. GutiĂ©rrez Meave, R. (2022). Framing and decisions: The punctuations of the mexican power generation policy subsystem. In A.-M. Bercu, I. Bilan, & C.-M. Apostoaie (Eds.), *European administrative area: Integration and resilience dynamics. Proceedings of the international @@ -791,7 +809,7 @@ Ioan Cuza” din Iai.
-85\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y +87\. GutiĂ©rrez Meave, R. (2022). *Redes de discurso, coaliciones y decisiones: La polĂ­tica de generaciĂłn elĂ©ctrica en mĂ©xico 1994–2018* \[PhD thesis, Centro de InvestigaciĂłn y Docencia EconĂłmicas (CIDE), Doctorado en PolĂ­ticas PĂșblicas\]. @@ -800,7 +818,7 @@ Doctorado en PolĂ­ticas PĂșblicas\].
-86\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and +88\. Gutiérrez-Meave, R. (2024). Advocacy coalitions, soft power, and policy change in Mexican electricity policy: A discourse network analysis. *Policy & Politics*. @@ -809,7 +827,7 @@ analysis. *Policy & Politics*.
-87\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on +89\. Hamanduna, A. O. L., & Widjanarko, P. (2023). Discourse network on the revision of Indonesian information and electronic transaction law. *Jurnal Studi Komunikasi*, *7*(2), 519–538. @@ -818,7 +836,7 @@ the revision of Indonesian information and electronic transaction law.
-88\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics +90\. Hanschmann, R. (2019). *Stalling the engine? EU climate politics after the “great recession.” Investigating the impact of economic shocks on EU climate policy-making in three case studies* \[Doctoral Dissertation, University of Potsdam, Faculty of Economics; Social @@ -828,7 +846,7 @@ Sciences\].
-89\. Hanschmann, R. (2017). Polarized business interests. EU climate +91\. Hanschmann, R. (2017). Polarized business interests. EU climate policy-making during the “great recession.” In D. K. Jesuit & R. A. Williams (Eds.), *Public policy, governance and polarization. Making governance work* (1st ed., pp. 126–156). Routledge. @@ -838,7 +856,7 @@ governance work* (1st ed., pp. 126–156). Routledge.
-90\. Hasselbalch, J. (2017). *The contentious politics of disruptive +92\. Hasselbalch, J. (2017). *The contentious politics of disruptive innovation: Vaping and fracking in the European Union* \[PhD thesis, University of Warwick, Department of Politics; International Studies; Université Libre de Bruxelles, Département de Sciences Politiques\]. @@ -848,7 +866,7 @@ Université Libre de Bruxelles, Département de Sciences Politiques\].
-91\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity +93\. Hasselbalch, J. A. (2019). Framing brain drain: Between solidarity and skills in European labor mobility. *Review of International Political Economy*, *26*(6), 1333–1360. @@ -857,7 +875,7 @@ Political Economy*, *26*(6), 1333–1360.
-92\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke +94\. Haunss, S. (2017). (De-)legitimating discourse networks: Smoke without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau (Eds.), *Capitalism and its legitimacy in times of crisis* (pp. 191–220). Palgrave Macmillan. @@ -867,7 +885,7 @@ without fire? In S. Schneider, H. Schmidtke, S. Haunss, & J. Gronau
-93\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der +95\. Haunss, S., Dietz, M., & Nullmeier, F. (2013). Der Ausstieg aus der Atomenergie: Diskursnetzwerkanalyse als Beitrag zur ErklĂ€rung einer radikalen Politikwende. *Zeitschrift fĂŒr Diskursforschung / Journal for Discourse Studies*, *1*(3), 288–316. @@ -877,7 +895,7 @@ Discourse Studies*, *1*(3), 288–316.
-94\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of +96\. Haunss, S., & Hollway, J. (2023). Multimodal mechanisms of political discourse dynamics and the case of Germany’s nuclear energy phase-out. *Network Science*, *11*(2), 205–223. @@ -886,7 +904,7 @@ phase-out. *Network Science*, *11*(2), 205–223.
-95\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, +97\. Haunss, S., Kuhn, J., Padó, S., Blessing, A., Blokker, N., Dayanik, E., & Lapesa, G. (2020). Integrating manual and automatic annotation for the creation of discourse network data sets. *Politics and Governance*, *8*(2), 326–339. @@ -895,7 +913,7 @@ the creation of discourse network data sets. *Politics and Governance*,
-96\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). +98\. Haunss, S., Lenke, F., Schmidtke, H., & Schneider, S. (2015). Finanzkrise ohne Legitimationskrise? Kapitalismuskritik in der deutschen QualitĂ€tspresse. In M. Dammayr, D. Grass, & B. RothmĂŒller (Eds.), *LegitimitĂ€t. Gesellschaftliche, politische und wissenschaftliche @@ -906,7 +924,7 @@ Bruchlinien in der Rechtfertigung* (pp. 73–94). Transcript.
-97\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive +99\. Hayward, B. A., McKay-Brown, L., & Poed, S. (2023). Restrictive practices and the “need” for positive behaviour support (PBS): A critical discourse examination of disability policy beliefs. *Journal of Intellectual Disabilities*, *27*(1), 170–189. @@ -916,7 +934,7 @@ Intellectual Disabilities*, *27*(1), 170–189.
-98\. Heermann, M. (2024). *Preferences and coalitions in European Union +100\. Heermann, M. (2024). *Preferences and coalitions in European Union internet policy* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -925,7 +943,7 @@ Department of Politics; Public Administration\].
-99\. Heiberg, J. (2022). *The geography of configurations that work* +101\. Heiberg, J. (2022). *The geography of configurations that work* \[Doctoral Dissertation, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -933,7 +951,7 @@ Sustainable Development\].
-100\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of +102\. Heiberg, J., Binz, C., & Truffer, B. (2020). The geography of technology legitimation: How multiscalar institutional dynamics matter for path creation in emerging industries. *Economic Geography*, *96*(5), 470–498. @@ -942,7 +960,7 @@ for path creation in emerging industries. *Economic Geography*, *96*(5),
-101\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions +103\. Heiberg, J., Truffer, B., & Binz, C. (2022). Assessing transitions through socio-technical configuration analysis – a methodological framework and a case study in the water sector. *Research Policy*, *51*(1), 104363. @@ -951,7 +969,7 @@ framework and a case study in the water sector. *Research Policy*,
-102\. Heinmiller, T. B. (2023). Have advocacy coalitions been +104\. Heinmiller, T. B. (2023). Have advocacy coalitions been difference-making in Canadian policy processes? Evidence from firearms policy processes in the 1970s and 1990s. *Canadian Political Science Review*, *17*(2), 1–17. @@ -961,7 +979,7 @@ Review*, *17*(2), 1–17.
-103\. Henrichsen, T. (2020). *Party competition as interdependent +105\. Henrichsen, T. (2020). *Party competition as interdependent process – assessing the contagion effect of Eurosceptic parties in Italy* \[PhD thesis, Sant’Anna School of Advanced Studies Pisa, Joint PhD in Political Science, European Politics; International Relations\]. @@ -971,7 +989,7 @@ PhD in Political Science, European Politics; International Relations\].
-104\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global +106\. Herranz-SurrallĂ©s, A. (2020). “Authority shifts” in global governance: Intersecting politicizations and the reform of investor–state arbitration. *Politics and Governance*, *8*(1), 336–347. @@ -980,7 +998,7 @@ investor–state arbitration. *Politics and Governance*, *8*(1), 336–347.
-105\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in +107\. Hertwig, M., & Witzak, P. (2022). Hybride Interessenvertretung in der Plattformökonomie. Herausforderungen des “coalition building” bei der Kooperation zwischen IG Metall und YouTubers Union. *Zeitschrift fĂŒr Soziologie*, *51*(2), 174–192. @@ -989,7 +1007,7 @@ Soziologie*, *51*(2), 174–192.
-106\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, +108\. Hilton, S., Buckton, C. H., Henrichsen, T., Fergie, G., & Leifeld, P. (2020). Policy congruence and advocacy strategies in the discourse networks of minimum unit pricing for alcohol and the soft drinks industry levy. *Addiction*, *115*(12), 2303–2314. @@ -999,7 +1017,7 @@ industry levy. *Addiction*, *115*(12), 2303–2314.
-107\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing +109\. Hodge, E. M., Benko, S. L., & Salloum, S. J. (2020). Tracing states’ messages about common core instruction: An analysis of English/language arts and close reading resources. *Teachers College Record*, *122*(3), 1–42. @@ -1008,7 +1026,7 @@ Record*, *122*(3), 1–42.
-108\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: +110\. Holma, K. (2021). *Suomesta yritysvastuun edellÀkÀvijÀ?: Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -1017,7 +1035,7 @@ Diskurssiverkostoanalyysi suomalaisesta yritysvastuukeskustelusta*
-109\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in +111\. Hopkins, V. (2020). *Lobbying for democracy: Interest groups in Canada’s parliamentary system* \[PhD thesis, Simon Fraser University, Department of Political Science\]. @@ -1025,7 +1043,7 @@ Department of Political Science\].
-110\. Horning, D. G. (2017). *Understanding structure and character in +112\. Horning, D. G. (2017). *Understanding structure and character in rural water governance networks* \[PhD thesis, University of British Columbia, College of Graduate Studies\]. @@ -1033,7 +1051,7 @@ Columbia, College of Graduate Studies\].
-111\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). +113\. Hornung, J., Schröder, I., & Bandelow, N. C. (2023). Programmatisches Handeln in der deutschen Verkehrspolitik. Gemeinsame IdentitĂ€ten von Akteuren im Umfeld des Deutschlandtakts. In D. Sack, H. Straßheim, & K. Zimmermann (Eds.), *Renaissance der Verkehrspolitik. @@ -1044,7 +1062,7 @@ Springer VS.
-112\. Howe, A. C. (2022). *Network processes related to political +114\. Howe, A. C. (2022). *Network processes related to political discourse and policy positions: The case of climate change policy networks in Canada* \[PhD thesis, University of British Columbia, Department of Sociology\]. @@ -1053,7 +1071,7 @@ Department of Sociology\].
-113\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media +115\. Howe, A. C., Stoddart, M. C. J., & Tindall, D. B. (2020). Media coverage and perceived policy influence of environmental actors: Good strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310. @@ -1062,7 +1080,7 @@ strategy or pyrrhic victory? *Politics and Governance*, *8*(2), 298–310.
-114\. Hullmann, C. (2023). *Case study on the German discourse of +116\. Hullmann, C. (2023). *Case study on the German discourse of industry decarbonization* \[Master's Thesis, Radboud University Nijmegen, Nijmegen School of Management\]. @@ -1071,7 +1089,7 @@ Nijmegen, Nijmegen School of Management\].
-115\. Hurka, S., & Nebel, K. (2013). Framing and policy change after +117\. Hurka, S., & Nebel, K. (2013). Framing and policy change after shooting rampages: A comparative analysis of discourse networks. *Journal of European Public Policy*, *20*(3), 390–406. @@ -1080,7 +1098,7 @@ shooting rampages: A comparative analysis of discourse networks.
-116\. Imbert, I. (2017). *An inquiry into the material and ideational +118\. Imbert, I. (2017). *An inquiry into the material and ideational dimensions of policymaking: A case study of fuel poverty in Germany* \[Doctoral Dissertation, University of Konstanz, Department of Politics; Public Administration\]. @@ -1090,7 +1108,7 @@ Public Administration\].
-117\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja +119\. JalasmÀki, H. (2020). *Taistelu asiantuntijuudesta: Uskomukset ja kannatuskoalitiot varhaiskasvatuksen diskurssiverkostossa* \[Master's Thesis, University of Helsinki, Faculty of Social Sciences\]. @@ -1099,7 +1117,7 @@ Thesis, University of Helsinki, Faculty of Social Sciences\].
-118\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). +120\. Janning, F., Leifeld, P., Malang, T., & Schneider, V. (2009). Diskursnetzwerkanalyse. Überlegungen zur Theoriebildung und Methodik. In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. @@ -1109,7 +1127,7 @@ V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.),
-119\. Jeong, M. (2017). *National renewable energy policy in a global +121\. Jeong, M. (2017). *National renewable energy policy in a global world* \[PhD thesis, University of Maryland, College Park, School of Public Policy\]. @@ -1118,7 +1136,7 @@ Public Policy\].
-120\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China +122\. Jin, Y., Schaub, S., Tosun, J., & Wesseler, J. (2022). Does China have a public debate on genetically modified organisms? A discourse network analysis of public debate on Weibo. *Public Understanding of Science*, *31*(6), 732–750. @@ -1127,7 +1145,7 @@ Science*, *31*(6), 732–750.
-121\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The +123\. Joshi, B., & Swarnakar, P. (2023). How fair is our air? The injustice of procedure, distribution, and recognition within the discourse of air pollution in Delhi, India. *Environmental Sociology*, *9*(2), 176–189. @@ -1136,7 +1154,7 @@ discourse of air pollution in Delhi, India. *Environmental Sociology*,
-122\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: +124\. Joshi, B., & Swarnakar, P. (2021). Staying away, staying alive: Exploring risk and stigma of COVID-19 in the context of beliefs, actors and hierarchies in India. *Current Sociology*, *69*(4), 492–511. @@ -1145,7 +1163,7 @@ and hierarchies in India. *Current Sociology*, *69*(4), 492–511.
-123\. Kammerer, M. (2017). *Climate politics at the intersection between +125\. Kammerer, M. (2017). *Climate politics at the intersection between international dynamics and national decision-making: A policy network approach* \[Doctoral Thesis, University of Zurich, Faculty of Arts; Social Sciences\]. @@ -1154,7 +1172,7 @@ Social Sciences\].
-124\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die +126\. Kammerer, M., Crameri, F., & Ingold, K. (2019). Das Klima und die EU: Eine Diskursperspektive auf die deutsche und schweizerische Klimapolitik. In R. Careja, P. Emmenegger, & N. Giger (Eds.), *The european social model under pressure. Liber amicorum in honour of klaus @@ -1165,7 +1183,7 @@ armingeon* (pp. 599–623). Springer VS.
-125\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate +127\. Kammerer, M., & Ingold, K. (2023). Actors and issues in climate change policy: The maturation of a policy discourse in the national and international context. *Social Networks*, *75*, 65–77. @@ -1174,7 +1192,7 @@ international context. *Social Networks*, *75*, 65–77.
-126\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat +128\. Kasih, P. C. (2023). Pertarungan wacana Kereta Cepat Jakarta-Bandung dalam media online. *Jurnal Ilmu Komunikasi UHO: Jurnal Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34. @@ -1183,7 +1201,7 @@ Penelitian Kajian Ilmu Komunikasi Dan Informasi*, *8*(1), 19–34.
-127\. Keller, S. (2023). *Analysis of the media discourse about meat and +129\. Keller, S. (2023). *Analysis of the media discourse about meat and meat substitutes in U.S. Media between 2016 and 2021* \[Master's Thesis, University of Bern, Faculty of Science, Oeschger Centre for Climate Change Research\]. @@ -1193,7 +1211,7 @@ Change Research\].
-128\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: +130\. Kenis, P., & Schneider, V. (2019). Analyzing policy-making II: Policy network analysis. In H. Van den Bulck, M. Puppis, K. Donders, & L. Van Audenhove (Eds.), *The Palgrave Handbook of methods for media policy research* (pp. 471–491). Palgrave Macmillan. @@ -1203,7 +1221,7 @@ policy research* (pp. 471–491). Palgrave Macmillan.
-129\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme +131\. Khatami, M. I. (2022). Discourse network analysis (DNA): Aktivisme digital dalam perdebatan isu “presiden tiga periode” di Twitter. *Jurnal Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94. @@ -1212,7 +1230,7 @@ Audience: Jurnal Ilmu Komunikasi*, *5*(1), 80–94.
-130\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? +132\. Khubbeeva, P. (2022). *Vom Bitcoin zur Blockchain? Distributed-Ledger-Technologien im politischen Diskurs. Leitbilder, Ideen und Diskursnetzwerke im deutschen Bundestag der 19. Legislaturperiode* \[Master's Thesis, FU Berlin, Otto-Suhr-Institut fĂŒr @@ -1222,7 +1240,7 @@ Politikwissenschaft\].
-131\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech +133\. KoďouskovĂĄ, H., & Lehotskỳ, L. (2021). Energy poverty in the Czech Republic: Individual responsibility or structural issue? *Energy Research & Social Science*, *72*, 101877. @@ -1231,7 +1249,7 @@ Research & Social Science*, *72*, 101877.
-132\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling +134\. Koebele, E. A., Bultema, S., & Weible, C. (2020). Modeling environmental governance in the Lake Tahoe basin: A multiplex network approach. In M. Fischer & K. Ingold (Eds.), *Networks in water governance* (pp. 173–202). Palgrave Macmillan. @@ -1241,7 +1259,7 @@ governance* (pp. 173–202). Palgrave Macmillan.
-133\. Komala, R. (2024). Jejaring wacana pada debat kebijakan publik di +135\. Komala, R. (2024). Jejaring wacana pada debat kebijakan publik di DPR RI: Isu perlindungan data pribadi. *Jurnal Komunikasi Global*, *13*(1), 175–198. @@ -1249,7 +1267,7 @@ DPR RI: Isu perlindungan data pribadi. *Jurnal Komunikasi Global*,
-134\. Kooistra, M. N. (2022). *Space security and orbital +136\. Kooistra, M. N. (2022). *Space security and orbital sustainability. An institutional logics approach* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -1258,7 +1276,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-135\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). +137\. Koop-Monteiro, Y., Stoddart, M. C. J., & Tindall, D. B. (2023). Animals and climate change: A visual and discourse network analysis of Instagram posts. *Environmental Sociology*, *9*(4), 409–426. @@ -1267,7 +1285,7 @@ Instagram posts. *Environmental Sociology*, *9*(4), 409–426.
-136\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors +138\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors and justifications in media debates on Arctic climate change in Finland and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117. @@ -1276,7 +1294,7 @@ and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117.
-137\. Kukkonen, A. (2018). *Discourse networks and justifications of +139\. Kukkonen, A. (2018). *Discourse networks and justifications of climate change policy: News media debates in Canada, the United States, Finland, France, Brazil and India* \[Doctoral Dissertation, University of Helsinki, Faculty of Social Sciences\]. @@ -1286,7 +1304,7 @@ of Helsinki, Faculty of Social Sciences\].
-138\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to +140\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to politicization of science: How the forestry coalition challenged the scientific consensus in the Finnish news media debate on increased logging. *Society & Natural Resources*, *37*(1), 91–112. @@ -1296,7 +1314,7 @@ logging. *Society & Natural Resources*, *37*(1), 91–112.
-139\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy +141\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy interface as a discourse network: Finland’s climate change policy 2002–2015. *Politics and Governance*, *8*(2), 200. @@ -1305,7 +1323,7 @@ interface as a discourse network: Finland’s climate change policy
-140\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy +142\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy coalitions, beliefs and climate change policy in the United States. *Public Administration*, *95*(3), 713–729. @@ -1314,7 +1332,7 @@ coalitions, beliefs and climate change policy in the United States.
-141\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., +143\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., Lahsen, M., & Stoddart, M. C. J. (2018). International organizations, advocacy coalitions, and domestication of global norms: Debates on climate change in Canada, the US, Brazil, and India. *Environmental @@ -1325,7 +1343,7 @@ Science & Policy*, *81*, 54–62.
-142\. Kurniawati, H. O. (2024). Analisis jejaring wacana Rancangan +144\. Kurniawati, H. O. (2024). Analisis jejaring wacana Rancangan Undang-Undang Kesehatan Tahun 2023 dan potensi Judicial Review ke Mahkamah Konstitusi. *UNES Law Review*, *6*(3), 8660–8675. @@ -1334,7 +1352,7 @@ Mahkamah Konstitusi. *UNES Law Review*, *6*(3), 8660–8675.
-143\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., +145\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., Kuhn, J., & PadĂł, S. (2020). DEbateNet-mig15: Tracing the 2015 immigration debate in Germany over time. *Proceedings of the Twelfth Language Resources and Evaluation Conference*, 919–927. @@ -1344,7 +1362,7 @@ Language Resources and Evaluation Conference*, 919–927.
-144\. Laurer, M., & Seidl, T. (2021). Regulating the European +146\. Laurer, M., & Seidl, T. (2021). Regulating the European data-driven economy: A case study on the general data protection regulation. *Policy & Internet*, *13*(2), 257–277. @@ -1353,7 +1371,7 @@ regulation. *Policy & Internet*, *13*(2), 257–277.
-145\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem +147\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem Discourse Network Analyzer (DNA). In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. 391–404). Springer VS. @@ -1363,7 +1381,7 @@ Visualisierungen* (pp. 391–404). Springer VS.
-146\. Leifeld, P. (2013). Reconceptualizing major policy change in the +148\. Leifeld, P. (2013). Reconceptualizing major policy change in the advocacy coalition framework: A discourse network analysis of German pension politics. *Policy Studies Journal*, *41*(1), 169–198. @@ -1372,7 +1390,7 @@ pension politics. *Policy Studies Journal*, *41*(1), 169–198.
-147\. Leifeld, P. (2014). Polarization of coalitions in an agent-based +149\. Leifeld, P. (2014). Polarization of coalitions in an agent-based model of political discourse. *Computational Social Networks*, *1*(1), 1–22. @@ -1380,7 +1398,7 @@ model of political discourse. *Computational Social Networks*, *1*(1),
-148\. Leifeld, P. (2016). *Policy debates as dynamic networks: German +150\. Leifeld, P. (2016). *Policy debates as dynamic networks: German pension politics and privatization discourse*. Campus. @@ -1388,7 +1406,7 @@ pension politics and privatization discourse*. Campus.
-149\. Leifeld, P. (2017). Discourse network analysis: Policy debates as +151\. Leifeld, P. (2017). Discourse network analysis: Policy debates as dynamic networks. In J. N. Victor, A. H. Montgomery, & M. N. Lubell (Eds.), *The Oxford Handbook of political networks* (pp. 301–325). Oxford University Press. @@ -1398,7 +1416,7 @@ Oxford University Press.
-150\. Leifeld, P. (2020). Policy debates and discourse network analysis: +152\. Leifeld, P. (2020). Policy debates and discourse network analysis: A research agenda. *Politics and Governance*, *8*(2), 180–183. @@ -1406,7 +1424,7 @@ A research agenda. *Politics and Governance*, *8*(2), 180–183.
-151\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition +153\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition formation in policy debates*. arXiv Preprint. @@ -1414,7 +1432,7 @@ formation in policy debates*. arXiv Preprint.
-152\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and +154\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and the conflict over software patents in Europe. *European Journal of Political Research*, *51*(3), 382–409. @@ -1423,7 +1441,7 @@ Political Research*, *51*(3), 382–409.
-153\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. +155\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. (2021). Belief system alignment and cross-sectoral advocacy efforts in policy debates. *Journal of European Public Policy*, 1–24. @@ -1432,7 +1450,7 @@ policy debates. *Journal of European Public Policy*, 1–24.
-154\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss +156\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss von Diskurskoalitionen auf Vermögensbesteuerung in Deutschland, 1995–2015: Eine Diskursnetzwerkanalyse von Policy-Wandel in der Steuerpolitik* \[Master's Thesis, FernUniversitĂ€t Hagen, FakultĂ€t fĂŒr @@ -1443,7 +1461,7 @@ Kultur- und Sozialwissenschaften, Institut fĂŒr Politikwissenschaft\].
-155\. Leipold, A. (2024). *Der Einfluss von Diskurskoalitionen auf die +157\. Leipold, A. (2024). *Der Einfluss von Diskurskoalitionen auf die Besteuerung von Unternehmen. Ein Vergleich von Pressedebatten und Anhörungen im Deutschen Bundestag zur Steuergesetzgebung seit 1965* \[Doctoral Dissertation, Leuphana UniversitĂ€t LĂŒneburg, FakultĂ€t @@ -1453,7 +1471,7 @@ Staatswissenschaften\].
-156\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in +158\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in nascent policy subsystems: Issue and instrument priorities across venues. *Policy Sciences*, *56*, 633–655. @@ -1462,7 +1480,7 @@ venues. *Policy Sciences*, *56*, 633–655.
-157\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., +159\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., Lagabrielle, E., Seen, D. L., & Degenne, P. (2017). Collaborative landscape research in Reunion Island: Using spatial modelling and simulation to support territorial foresight and urban planning. *Applied @@ -1472,7 +1490,7 @@ Geography*, *78*, 66–77.
-158\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s +160\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s family planning: Perspectives of advocacy coalitions. *International Journal of Environmental Research and Public Health*, *20*(6), 5204. @@ -1481,7 +1499,7 @@ Journal of Environmental Research and Public Health*, *20*(6), 5204.
-159\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, +161\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, M. (2023). Slovak MPs’ response to the 2022 Russian invasion of Ukraine in light of conspiracy theories and the polarization of political discourse. *Humanities and Social Sciences Communications*, *10*(1), @@ -1491,7 +1509,7 @@ discourse. *Humanities and Social Sciences Communications*, *10*(1),
-160\. Ličková, V. (2023). *Coal framing in the Indian political +162\. Ličková, V. (2023). *Coal framing in the Indian political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1499,7 +1517,7 @@ Studies\].
-161\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in +163\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in the acceleration phase of the European energy transition. *Environmental Innovation and Societal Transitions*, *40*, 262–282. @@ -1508,7 +1526,7 @@ Innovation and Societal Transitions*, *40*, 262–282.
-162\. Lockhart, C. (2014). *Discourse network analysis of the Northern +164\. Lockhart, C. (2014). *Discourse network analysis of the Northern Gateway Pipeline project: Assessing environmental governance in the joint review panel process* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental @@ -1518,7 +1536,7 @@ Governance Section\].
-163\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & +165\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & Wagner, P. M. (2021). Unity in diversity? When advocacy coalitions and policy beliefs grow trees in South Africa. *Land Use Policy*, *102*, 105283. @@ -1527,7 +1545,7 @@ policy beliefs grow trees in South Africa. *Land Use Policy*, *102*,
-164\. MalkamÀki, A. (2019). *On the human impacts and governance of +166\. MalkamÀki, A. (2019). *On the human impacts and governance of large-scale tree plantations* \[Doctoral Dissertation, University of Helsinki, Faculty of Agriculture; Forestry\]. @@ -1536,7 +1554,7 @@ Helsinki, Faculty of Agriculture; Forestry\].
-165\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & +167\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & YlÀ-Anttila, T. (2023). *Complex coalitions: Political alliances across relational contexts*. arXiv:2308.14422. @@ -1545,7 +1563,7 @@ relational contexts*. arXiv:2308.14422.
-166\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & +168\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & YlĂ€-Anttila, T. (2021). On the acoustics of policy learning: Can co-participation in policy forums break up echo chambers? *Policy Studies Journal*, *49*(2), 431–456. @@ -1554,7 +1572,7 @@ Studies Journal*, *49*(2), 431–456.
-167\. Mardiah, A. N. R. (2018). *Interface between disaster and +169\. Mardiah, A. N. R. (2018). *Interface between disaster and development: Local economic revival through collaborative post-disaster recovery governance and network in Indonesia* \[PhD thesis, University of Leeds, School of Geography\]. @@ -1564,7 +1582,7 @@ of Leeds, School of Geography\].
-168\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward +170\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward integrated and inclusive disaster risk reduction in Indonesia: Review of regulatory frameworks and institutional networks. In R. Djalante, M. Garschagen, F. Thomalla, & R. Shaw (Eds.), *Disaster risk reduction in @@ -1575,7 +1593,7 @@ Indonesia* (pp. 57–84). Springer.
-169\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. +171\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. A., & Hodgson, D. J. (2023). Stakeholder discourse coalitions and polarisation in the hen harrier conservation debate in news media. *People and Nature*, *5*(2), 668–683. @@ -1585,7 +1603,7 @@ polarisation in the hen harrier conservation debate in news media.
-170\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the +172\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the networking structuration processes of the urban resilience concept in Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1), 46–57. @@ -1594,7 +1612,7 @@ Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1),
-171\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing +173\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing transitions through the lens of discourse networks: Coal phase-out in Germany. *Environmental Innovation and Societal Transitions*, *40*, 315–331. @@ -1603,7 +1621,7 @@ Germany. *Environmental Innovation and Societal Transitions*, *40*,
-172\. Mastroianni, L. (2024). How do crises affect policy subsystems? +174\. Mastroianni, L. (2024). How do crises affect policy subsystems? The evolution of policy core beliefs in the EU asylum policy. *JCMS: Journal of Common Market Studies*. @@ -1611,7 +1629,7 @@ Journal of Common Market Studies*.
-173\. Mayer, C. D. (2022). *New west tension and threatened species +175\. Mayer, C. D. (2022). *New west tension and threatened species protection: The western Joshua tree conservation debate in the Morongo Basin, California* \[Master's Thesis, California State University, Long Beach, Department of Geography\]. @@ -1621,7 +1639,7 @@ Beach, Department of Geography\].
-174\. McDonald, E. (2019). *Energy security in the age of +176\. McDonald, E. (2019). *Energy security in the age of interconnection: Cyber-threat framing in British political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1630,7 +1648,7 @@ interconnection: Cyber-threat framing in British political discourse*
-175\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of +177\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of floating signifier: Discourses and network analysis in the bioeconomy policy processes in Argentina and Uruguay. *Forest Policy and Economics*, *154*, 103039. @@ -1640,7 +1658,7 @@ Economics*, *154*, 103039.
-176\. Miles, A. (2020). *Changes in social networks and narratives +178\. Miles, A. (2020). *Changes in social networks and narratives associated with Lake Erie water quality management after the 2014 Toledo water crisis* \[Master's Thesis, The Ohio State University, Graduate Program in Environment; Natural Resources\]. @@ -1650,7 +1668,7 @@ Program in Environment; Natural Resources\].
-177\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear +179\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear energy in the European Union and its implications* \[Doctoral Dissertation, Masaryk University, Department of International Relations; European Studies\]. @@ -1659,7 +1677,7 @@ European Studies\].
-178\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes +180\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes diffuse in space – explaining a missed transition in San Diego’s water sector. *Environmental Innovation and Societal Transitions*, *44*, 29–47. @@ -1668,7 +1686,7 @@ sector. *Environmental Innovation and Societal Transitions*, *44*,
-179\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, +181\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, renewable energy, or both? Public opinion on small hydropower plants in Serbia. *Post-Communist Economies*, *34*(5), 684–713. @@ -1677,7 +1695,7 @@ Serbia. *Post-Communist Economies*, *34*(5), 684–713.
-180\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). +182\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). Relational coupling of multiple streams: The case of COVID-19 infections in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374. @@ -1686,7 +1704,7 @@ in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374.
-181\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? +183\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? For whom? Intersectionality in Canada’s feminist international assistance policy. *International Journal*, *75*(3), 329–348. @@ -1695,7 +1713,7 @@ assistance policy. *International Journal*, *75*(3), 329–348.
-182\. Muller, A. (2014). Het meten van discourscoalities met +184\. Muller, A. (2014). Het meten van discourscoalities met discoursnetwerkanalyse: Naar een formele analyse van het politieke vertoog. *Res Publica*, *56*(3), 337–364. @@ -1704,7 +1722,7 @@ vertoog. *Res Publica*, *56*(3), 337–364.
-183\. Muller, A. (2015). Using discourse network analysis to measure +185\. Muller, A. (2015). Using discourse network analysis to measure discourse coalitions: Towards a formal analysis of political discourse. *World Political Science*, *11*(2), 377–404. @@ -1713,7 +1731,7 @@ discourse coalitions: Towards a formal analysis of political discourse.
-184\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big +186\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big tobacco? Discourse network analysis of the cigarette advertising debate and policy in Indonesia. *Journal of Communication Inquiry*. @@ -1722,7 +1740,7 @@ and policy in Indonesia. *Journal of Communication Inquiry*.
-185\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine +187\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS. @@ -1730,7 +1748,7 @@ Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS.
-186\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den +188\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 153–177). transcript Verlag. @@ -1740,7 +1758,7 @@ politische Konzepte* (pp. 153–177). transcript Verlag.
-187\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere +189\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 77–98). transcript @@ -1750,7 +1768,7 @@ Verlag.
-188\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level +190\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level governance dynamics from a discourse network perspective: The debate over air pollution regulation in Germany. *Environmental Sciences Europe*, *34*(62), 1–18. @@ -1759,7 +1777,7 @@ Europe*, *34*(62), 1–18.
-189\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A +191\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A discourse network analysis of the evolution of the conflict over Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700. @@ -1768,7 +1786,7 @@ Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700.
-190\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate +192\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate action: Comparing the evolution of narratives using the “narrative rate” index. *Review of Policy Research*, *40*(6), 1093–1119. @@ -1777,7 +1795,7 @@ index. *Review of Policy Research*, *40*(6), 1093–1119.
-191\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die +193\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS. @@ -1785,7 +1803,7 @@ Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS.
-192\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in +194\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in South Korea’s nuclear power policy making: An advocacy coalition framework approach to policy knowledge. *Politics & Policy*, *51*(2), 201–221. @@ -1794,7 +1812,7 @@ framework approach to policy knowledge. *Politics & Policy*, *51*(2),
-193\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames +195\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames of advocacy coalitions in South Korea’s nuclear energy policy. *Review of Policy Research*, *39*(4), 387–410. @@ -1803,7 +1821,7 @@ of Policy Research*, *39*(4), 387–410.
-194\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position +196\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position und Einfluss der Kirchen in der deutschen Debatte um die embryonale Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.), *Religionspolitik und Politik der Religionen in Deutschland* (pp. @@ -1813,7 +1831,7 @@ Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.),
-195\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting +197\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting in emerging economies – a systematic literature review and research agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820. @@ -1822,7 +1840,7 @@ agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820.
-196\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ +198\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ opozice vƯči hlubinnĂ©mu ĂșloĆŸiĆĄti radioaktivnĂ­ch odpadĆŻ v ČeskĂ© republice* \[PhD thesis, Masarykova univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\]. @@ -1831,7 +1849,7 @@ univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\].
-197\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech +199\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech newspaper coverage: A one man show. In D. Tindall, M. C. J. Stoddart, & R. E. Dunlap (Eds.), *Handbook of anti-environmentalism* (pp. 84–106). Edward Elgar Publishing. @@ -1840,7 +1858,7 @@ Edward Elgar Publishing.
-198\. Ohlendorf, N. (2022). *The political economy of energy +200\. Ohlendorf, N. (2022). *The political economy of energy transitions* \[Doctoral Thesis, Technical University of Berlin, FakultĂ€t VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\]. @@ -1849,7 +1867,7 @@ VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\].
-199\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in +201\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in multi-sector transitions – discourse analysis on hydrogen in Germany. *Environmental Innovation and Societal Transitions*, *47*, 100692. @@ -1858,7 +1876,7 @@ multi-sector transitions – discourse analysis on hydrogen in Germany.
-200\. Ohno, T. (2022). Advocacy coalition framework in environmental +202\. Ohno, T. (2022). Advocacy coalition framework in environmental governance studies: Explaining major policy change for a large dam removal in Japan. *International Review of Public Policy*, *4*(1). @@ -1867,7 +1885,7 @@ removal in Japan. *International Review of Public Policy*, *4*(1).
-201\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa +203\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa na polĂ­tica de transporte marĂ­timo de cargas no Brasil: Privatização, descentralização e abertura para o capital estrangeiro. *Caderno CRH – Revista de CiĂȘncias Sociais Do Centro de Estudos Pesquisas e Humanidades @@ -1878,7 +1896,7 @@ Da Universidade Federal Da Bahia*, *36*(2), 1–21.
-202\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of +204\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of oil and gas governance in Ghana* \[PhD thesis, University of Colorado Denver, School of Public Affairs\]. @@ -1887,7 +1905,7 @@ Denver, School of Public Affairs\].
-203\. Osei-Kojo, A. (2023). Analysing the stability of advocacy +205\. Osei-Kojo, A. (2023). Analysing the stability of advocacy coalitions and policy frames in Ghana’s oil and gas governance. *Policy & Politics*, *51*(1), 71–90. @@ -1896,7 +1914,7 @@ coalitions and policy frames in Ghana’s oil and gas governance. *Policy
-204\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, +206\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, B. (2018). Natural gas market integration in the Visegrad 4 region: An example to follow or to avoid? *Energy Policy*, *112*, 184–197. @@ -1905,7 +1923,7 @@ example to follow or to avoid? *Energy Policy*, *112*, 184–197.
-205\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & +207\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & Kuhn, J. (2019). Who sides with whom? Towards computational construction of discourse networks for political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics*, @@ -1915,7 +1933,7 @@ Annual Meeting of the Association for Computational Linguistics*,
-206\. Palladino, N. (2021). The role of epistemic communities in the +208\. Palladino, N. (2021). The role of epistemic communities in the “constitutionalization” of internet governance: The example of the European Commission high-level expert group on artificial intelligence. *Telecommunications Policy*, *45*(6), 102149. @@ -1925,7 +1943,7 @@ European Commission high-level expert group on artificial intelligence.
-207\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, +209\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, P. (2020). Interactions among sustainable development goals: Knowledge for identifying multipliers and virtuous cycles. *Sustainable Development*, *28*(5), 1236–1250. @@ -1934,7 +1952,7 @@ Development*, *28*(5), 1236–1250.
-208\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des +210\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des Ă©chelles de la sĂ©curitĂ© dans une rĂ©gion Ă  gĂ©omĂ©trie variable* \[PhD thesis, UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\]. @@ -1944,7 +1962,7 @@ UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\].
-209\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and +211\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and climate change. A complex reflexive systems approach to energy transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.), *The routledge handbook of ideology and international relations* (pp. @@ -1954,7 +1972,7 @@ transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.),
-210\. PlĂŒmer, S. (2024). *Kongruentes Policy-Lernen als lernbedingter +212\. PlĂŒmer, S. (2024). *Kongruentes Policy-Lernen als lernbedingter Policy-Wandel. Zum Koordinationsmechanismus des Policy-Lernens in Regierungsformationen*. Springer VS. @@ -1963,7 +1981,7 @@ Regierungsformationen*. Springer VS.
-211\. A., P. I., Alekseev, D. V., & Zaripov, N. A. (2024). “Not by deed, +213\. A., P. I., Alekseev, D. V., & Zaripov, N. A. (2024). “Not by deed, but by word”: A discourse network analysis of parliamentary discussions of the bill on raising the retirement age in Russia. *Monitoring of Public Opinion: Economic and Social Changes*, *2*, 116–140. @@ -1973,7 +1991,7 @@ Public Opinion: Economic and Social Changes*, *2*, 116–140.
-212\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing +214\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing psychedelics: The segregation of spiritual and scientific narratives within the r/microdosing online community. *Journal of Psychedelic Studies*, *7*(2), 119–128. @@ -1982,7 +2000,7 @@ Studies*, *7*(2), 119–128.
-213\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse +215\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse network analysis of French hydrogen politics* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1990,7 +2008,7 @@ University, Faculty of Social Studies\].
-214\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking +216\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking analysis as alternative research method in communication science studies – discourse networking analysis sebagai metode penelitian alternatif dalam kajian ilmu komunikasi. *Jurnal Penelitian Komunikasi Dan Opini @@ -2000,7 +2018,7 @@ Publik*, *21*(2), 126–136.
-215\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). +217\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). Discourse network analysis pada stakeholder dan integrated value creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*, *7*(2), 256–274. @@ -2009,7 +2027,7 @@ creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*,
-216\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., +218\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., Espressivo, A., Herlinda, O., Meilissa, Y., & Saminarsih, D. S. (2023). The advocacy coalition of sugar-sweetened beverage taxes in Indonesia. *BMJ Global Health*, *8*(Suppl 8), 1–13. @@ -2019,7 +2037,7 @@ The advocacy coalition of sugar-sweetened beverage taxes in Indonesia.
-217\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder +219\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder environmental governance in action: REDD+ discourse coalitions in Tanzania. *Ecology and Society*, *19*(2), 66–76. @@ -2028,7 +2046,7 @@ Tanzania. *Ecology and Society*, *19*(2), 66–76.
-218\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education +220\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education policy discourse: Philanthropic funders as entrepreneurs in policy networks. *Interest Groups & Advocacy*, *7*(3), 258–288. @@ -2037,7 +2055,7 @@ networks. *Interest Groups & Advocacy*, *7*(3), 258–288.
-219\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the +221\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the political economy of knowledge production shapes education policy: The case of teacher evaluation in federal policy discourse. *Educational Evaluation and Policy Analysis*, *43*(3), 472–494. @@ -2047,7 +2065,7 @@ Evaluation and Policy Analysis*, *43*(3), 472–494.
-220\. Rennkamp, B. (2019). Power, coalitions and institutional change in +222\. Rennkamp, B. (2019). Power, coalitions and institutional change in South African climate policy. *Climate Policy*, *19*(6), 756–770. @@ -2055,7 +2073,7 @@ South African climate policy. *Climate Policy*, *19*(6), 756–770.
-221\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. +223\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. (2017). Competing coalitions: The politics of renewable energy and fossil fuels in Mexico, South Africa and Thailand. *Energy Research & Social Science*, *34*, 214–223. @@ -2065,7 +2083,7 @@ Social Science*, *34*, 214–223.
-222\. Rinscheid, A. (2018). *Behavioral and institutionalist +224\. Rinscheid, A. (2018). *Behavioral and institutionalist perspectives on preference formation in a contested political context: The case of divesting from nuclear power* \[Doctoral Dissertation, University of St. Gallen, School of Management, Economics, Law, Social @@ -2076,7 +2094,7 @@ Sciences; International Affairs\].
-223\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy +225\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy change: Exploring the role of subsystem polarization in nuclear energy policymaking. *European Policy Analysis*, *1*(2), 34–70. @@ -2085,7 +2103,7 @@ policymaking. *European Policy Analysis*, *1*(2), 34–70.
-224\. Rinscheid, A. (2020). Business power in noisy politics: An +226\. Rinscheid, A. (2020). Business power in noisy politics: An exploration based on discourse network analysis and survey data. *Politics and Governance*, *8*(2), 286–297. @@ -2094,7 +2112,7 @@ exploration based on discourse network analysis and survey data.
-225\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. +227\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. (2020). Why do junctures become critical? Political discourse, agency, and joint belief shifts in comparative perspective. *Regulation & Governance*, *14*(4), 653–673. @@ -2103,7 +2121,7 @@ Governance*, *14*(4), 653–673.
-226\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The +228\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The diffusion of protest against the anti-counterfeiting trade agreement in the age of austerity* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2113,7 +2131,7 @@ Department of Political; Social Sciences\].
-227\. Rone, J. (2023). Instrumentalising sovereignty claims in British +229\. Rone, J. (2023). Instrumentalising sovereignty claims in British pro- and anti-Brexit mobilisations. *The British Journal of Politics and International Relations*, *25*(3), 444–461. @@ -2122,7 +2140,7 @@ International Relations*, *25*(3), 444–461.
-228\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff +230\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff increase policy in discourse analysis network. *Jurnal Komunikasi*, *17*(1), 62–75. @@ -2130,7 +2148,7 @@ increase policy in discourse analysis network. *Jurnal Komunikasi*,
-229\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, +231\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, right now: Storylines and social identities in coalition building in a local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247. @@ -2139,7 +2157,7 @@ local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247.
-230\. Samatan, N., Nurhasanah, N., Londol, M., Robingah, R., & Fania, S. +232\. Samatan, N., Nurhasanah, N., Londol, M., Robingah, R., & Fania, S. (2024). Critical discourse network analysis on Ferdy Sambo news in online media using agenda setting theory. *Jurnal Komunikasi Ikatan Sarjana Komunikasi Indonesia*, *9*(1), 146–167. @@ -2149,7 +2167,7 @@ Sarjana Komunikasi Indonesia*, *9*(1), 146–167.
-231\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, +233\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, R. R. (2023). Bias media mengenai pemberitaan batalnya Indonesia menjadi tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co. *Jurnal Ilmiah Wahana Pendidikan*, *9*(23), 547–556. @@ -2159,7 +2177,7 @@ tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co.
-232\. Schaub, S. (2021). *The politics of water protection* \[Doctoral +234\. Schaub, S. (2021). *The politics of water protection* \[Doctoral Dissertation, University of Heidelberg, Faculty of Economics; Social Studies, Institute of Political Science\]. @@ -2168,7 +2186,7 @@ Studies, Institute of Political Science\].
-233\. Schaub, S. (2021). Public contestation over agricultural +235\. Schaub, S. (2021). Public contestation over agricultural pollution: A discourse network analysis on narrative strategies in the policy process. *Policy Sciences*, *54*(4), 783–821. @@ -2177,7 +2195,7 @@ policy process. *Policy Sciences*, *54*(4), 783–821.
-234\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable +236\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable pharmacy? The influence of public debates on policy responses to pharmaceutical contaminants in water. *Environmental Sciences Europe*, *32*(1), 1–19. @@ -2186,7 +2204,7 @@ pharmaceutical contaminants in water. *Environmental Sciences Europe*,
-235\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy +237\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy network approaches: Evidence from water policy on micropollutants. *Politics and Governance*, *8*(2), 184–199. @@ -2195,7 +2213,7 @@ network approaches: Evidence from water policy on micropollutants.
-236\. Schmid, N. (2020). *The politics of technological change – case +238\. Schmid, N. (2020). *The politics of technological change – case studies from the energy sector* \[Doctoral Thesis, ETH ZĂŒrich, Department of Humanities, Social; Political Sciences, Energy; Technology Policy Group\]. @@ -2204,7 +2222,7 @@ Policy Group\].
-237\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining +239\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining advocacy coalition change with policy feedback. *Policy Studies Journal*, *48*(4), 1109–1134. @@ -2212,7 +2230,7 @@ Journal*, *48*(4), 1109–1134.
-238\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A +240\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A discourse network analysis of German parliamentary debates* \[Diploma Thesis, Charles University, Department of Security Studies\]. @@ -2221,7 +2239,7 @@ Thesis, Charles University, Department of Security Studies\].
-239\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, +241\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, partisanship and paradigmatic change in energy policy – analyzing parliamentary discourse in Germany over 30 years. *Climate Policy*, *19*(6), 771–786. @@ -2230,7 +2248,7 @@ parliamentary discourse in Germany over 30 years. *Climate Policy*,
-240\. Schmitz, L. (2018). *From coherence to coheritization. Explaining +242\. Schmitz, L. (2018). *From coherence to coheritization. Explaining the rise of policy coherence in EU external policy* \[Master's Thesis, Radboud University Nijmegen, Faculteit der Managementwetenschappen\]. @@ -2239,7 +2257,7 @@ Radboud University Nijmegen, Faculteit der Managementwetenschappen\].
-241\. Schmitz, L., & Eimer, T. R. (2020). From coherence to +243\. Schmitz, L., & Eimer, T. R. (2020). From coherence to coheritization: Explaining the rise of policy coherence in EU external policy. *Globalizations*, *17*(4), 629–647. @@ -2248,7 +2266,7 @@ policy. *Globalizations*, *17*(4), 629–647.
-242\. Schmitz, L., & Seidl, T. (2022). As open as possible, as +244\. Schmitz, L., & Seidl, T. (2022). As open as possible, as autonomous as necessary: Understanding the rise of open strategic autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*, *61*(3), 834–852. @@ -2257,7 +2275,7 @@ autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*,
-243\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of +245\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of technology legitimacy: Contending storylines surrounding wind energy in Austria and Switzerland. *Technological Forecasting and Social Change*, *198*, 122929. @@ -2266,7 +2284,7 @@ Austria and Switzerland. *Technological Forecasting and Social Change*,
-244\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and +246\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and displacements in policy discourse: The climate change issue in Germany 2007–2010. In S. Silvern & S. Young (Eds.), *Environmental change and sustainability* (pp. 157–184). Intech. @@ -2275,7 +2293,7 @@ sustainability* (pp. 157–184). Intech.
-245\. Schulz, C. (2020). Forest conservation through markets? A +247\. Schulz, C. (2020). Forest conservation through markets? A discourse network analysis of the debate on funding mechanisms for REDD+ in Brazil. *Environmental Communication*, *14*(2), 202–218. @@ -2284,7 +2302,7 @@ in Brazil. *Environmental Communication*, *14*(2), 202–218.
-246\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of +248\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of Twitter and newspapers: Lessons learned from the nuclear debate in the 2022 French presidential campaign. *French Politics*, *21*(2), 195–221. @@ -2293,7 +2311,7 @@ Twitter and newspapers: Lessons learned from the nuclear debate in the
-247\. Sconfienza, U. M., & Durand, F. (2024). Contested nuclear +249\. Sconfienza, U. M., & Durand, F. (2024). Contested nuclear discourses during the 2022 French presidential campaign. *Journal of Environmental Policy & Planning*. @@ -2302,7 +2320,7 @@ Environmental Policy & Planning*.
-248\. Seidl, T. (2021). *Ideas, politics, and technological change: +250\. Seidl, T. (2021). *Ideas, politics, and technological change: Essays on the comparative political economy of digital capitalism* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2311,7 +2329,7 @@ Sciences\].
-249\. Seidl, T. (2022). The politics of platform capitalism: A case +251\. Seidl, T. (2022). The politics of platform capitalism: A case study on the regulation of Uber in New York. *Regulation & Governance*, *16*(2), 357–374. @@ -2319,7 +2337,7 @@ study on the regulation of Uber in New York. *Regulation & Governance*,
-250\. Selle, L. (2017). What multi-level parliamentary system? +252\. Selle, L. (2017). What multi-level parliamentary system? Parliamentary discourses in EU budgetary negotiations (MFF 2014–2020). In S. Becker, M. W. Bauer, & A. De Feo (Eds.), *The new politics of the European Union budget* (pp. 149–172). Nomos. @@ -2329,7 +2347,7 @@ European Union budget* (pp. 149–172). Nomos.
-251\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z +253\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2337,7 +2355,7 @@ University, Faculty of Social Studies\].
-252\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris +254\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris India: Unpacking consensus and conflict through storylines and discourse coalitions. *Energy Research & Social Science*, *91*, 102687. @@ -2346,7 +2364,7 @@ coalitions. *Energy Research & Social Science*, *91*, 102687.
-253\. Shukla, R., & Swarnakar, P. (2022). Energy transition and +255\. Shukla, R., & Swarnakar, P. (2022). Energy transition and dialectics: Tracing discursive resistance to coal through discourse coalition in India. *Globalizations*, *20*(8), 1296–1311. @@ -2355,7 +2373,7 @@ coalition in India. *Globalizations*, *20*(8), 1296–1311.
-254\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi +256\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi virus corona dengan discourse network analysis. *Jurnal Kebijakan Kesehatan Indonesia: JKKI*, *9*(2), 98–106. @@ -2364,7 +2382,7 @@ Kesehatan Indonesia: JKKI*, *9*(2), 98–106.
-255\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying +257\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying influence on EU efforts to regulate CO2 emissions of cars using network analysis. *Interest Groups & Advocacy*, *12*, 388–412. @@ -2373,7 +2391,7 @@ analysis. *Interest Groups & Advocacy*, *12*, 388–412.
-256\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan +258\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 34–49. @@ -2381,7 +2399,7 @@ UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2),
-257\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada +259\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina. *Kommunikatif*, *12*(1), 37–50. @@ -2389,7 +2407,7 @@ kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina.
-258\. Sohn, C. (2023). The impact of rebordering on cross-border +260\. Sohn, C. (2023). The impact of rebordering on cross-border cooperation actors’ discourses in the Öresund region. A semantic network approach. *Geografiska Annaler: Series B, Human Geography*. @@ -2398,7 +2416,7 @@ approach. *Geografiska Annaler: Series B, Human Geography*.
-259\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu +261\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*, *7*(2), 130–145. @@ -2406,7 +2424,7 @@ ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*,
-260\. Stancioff, C. E. (2016). Locality and landscape change: Cultural +262\. Stancioff, C. E. (2016). Locality and landscape change: Cultural values and social-ecological resiliency in the Kalinago territory. In T. Collins, G. Kindermann, C. Newman, & N. Cronin (Eds.), *Landscape values: Place and praxis. Conference, galway, 29th june–2nd july, 2016* @@ -2417,7 +2435,7 @@ values: Place and praxis. Conference, galway, 29th june–2nd july, 2016*
-261\. Starke, J. (2016). *Generating policy change in situations of +263\. Starke, J. (2016). *Generating policy change in situations of equilibrium: Shifting discourse networks in the case of wild circus animals in Germany* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance @@ -2427,7 +2445,7 @@ Section\].
-262\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. +264\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. J., & Termeer, K. J. A. M. (2023). “Green future” versus “planetary boundaries”? Evolving online discourse coalitions in European bioeconomy conflicts. *Journal of Cleaner Production*, *425*, 139058. @@ -2437,7 +2455,7 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
-263\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +265\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2445,7 +2463,7 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-264\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +266\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\]. @@ -2454,7 +2472,7 @@ of Governance, Master of Public Policy\].
-265\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +267\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2463,7 +2481,7 @@ co-exist?* Palgrave Macmillan.
-266\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +268\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2473,7 +2491,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-267\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +269\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2483,7 +2501,7 @@ alignments through discourse networks and intersectoral relationships.
-268\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +270\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2492,7 +2510,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-269\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +271\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2501,7 +2519,7 @@ Sociologie*, *53*(3), 316–336.
-270\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +272\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2510,7 +2528,7 @@ and the cultural dynamics of multilevel climate governance.
-271\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +273\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2520,7 +2538,7 @@ climate change: Canadian national news and mediated policy networks.
-272\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +274\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy Research*, *40*(6), 1144–1168. @@ -2529,7 +2547,7 @@ Research*, *40*(6), 1144–1168.
-273\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +275\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2538,7 +2556,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-274\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +276\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2547,7 +2565,7 @@ Economics and Environment*, *83*(4), 62–75.
-275\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +277\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2556,7 +2574,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-276\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +278\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2566,7 +2584,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-277\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +279\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2574,7 +2592,7 @@ thesis, Utrecht University\].
-278\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +280\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2583,7 +2601,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-279\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +281\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2592,7 +2610,7 @@ International Conference on Business and Public Administration (AICoBPA
-280\. Taranger, K. K. (2020). *The institutionalisation of climate +282\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2601,7 +2619,7 @@ Environmental Governance Section\].
-281\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +283\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2610,7 +2628,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-282\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +284\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2620,7 +2638,7 @@ Glasgow, School of Social; Political Sciences\].
-283\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +285\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates on hydraulic fracturing. Comparing coalition politics in North America @@ -2631,7 +2649,7 @@ and Europe* (pp. 177–200). Palgrave Macmillan.
-284\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +286\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2640,7 +2658,7 @@ of Policy Research*, *34*(3), 310–330.
-285\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +287\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2649,7 +2667,7 @@ tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu*
-286\. Tuinenburg, J. (2019). *The effect of discourse networks on the +288\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2658,7 +2676,7 @@ Governance\].
-287\. Umansky Casapa, N. (2022). *Securitization and social media +289\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2667,7 +2685,7 @@ Dublin, School of Politics; International Relations\].
-288\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +290\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2675,7 +2693,7 @@ Social Studies\].
-289\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level +291\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level rise in a polycentric system. Easier said than done*. Cambridge University Press. @@ -2683,7 +2701,7 @@ University Press.
-290\. Vaughan, M. (2020). Talking about tax: The discursive distance +292\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2692,7 +2710,7 @@ Politics*, *17*(2), 114–129.
-291\. Vedres, B. (2022). Multivocality and robust action dynamics in +293\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2700,7 +2718,7 @@ political discourse. *Poetics*, *90*, 101576.
-292\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +294\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2709,7 +2727,7 @@ Environmental Change*, *63*, 102117.
-293\. Vogeler, C. S. (2022). The integration of environmental objectives +295\. Vogeler, C. S. (2022). The integration of environmental objectives in the common agricultural policy – partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2718,7 +2736,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-294\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +296\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2727,7 +2745,7 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-295\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +297\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343. @@ -2736,7 +2754,7 @@ the European Parliament. *European Policy Analysis*, *7*, 324–343.
-296\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of +298\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of the far right: How far-right actors become mainstream in public debates. *Political Communication*, *41*(3), 353–372. @@ -2745,7 +2763,7 @@ the far right: How far-right actors become mainstream in public debates.
-297\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +299\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2756,7 +2774,7 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
-298\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of +300\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of failure on socio-technical system configurations* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -2765,7 +2783,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-299\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +301\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2774,7 +2792,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-300\. Wallaschek, S. (2019). The discursive appeal to solidarity and +302\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2782,7 +2800,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-301\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +303\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2792,7 +2810,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-302\. Wallaschek, S. (2020). Analyzing the European parliamentary +304\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2802,7 +2820,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-303\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +305\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2811,7 +2829,7 @@ European Public Policy*, *27*(7), 1034–1053.
-304\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +306\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2820,7 +2838,7 @@ Economy*, *25*(2), 231–247.
-305\. Wallaschek, S. (2020). The discursive construction of solidarity: +307\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2828,7 +2846,7 @@ Studies*, *68*(1), 74–92.
-306\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +308\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2836,7 +2854,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-307\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +309\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2846,7 +2864,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-308\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +310\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2855,7 +2873,7 @@ the public sphere: A discourse network analysis of German newspapers
-309\. Wang, S. (2018). *Dynamic constructed climate change discourses +311\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2865,7 +2883,7 @@ Politics\].
-310\. Wang, S. (2024). *Communicating climate change in China. A dynamic +312\. Wang, S. (2024). *Communicating climate change in China. A dynamic discourse approach*. Palgrave Macmillan. @@ -2873,7 +2891,7 @@ discourse approach*. Palgrave Macmillan.
-311\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +313\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2882,7 +2900,7 @@ Public Policy*, *44*(3), 354–368.
-312\. Wang, Y. (2021). Examining the actor coalitions and discourse +314\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2891,7 +2909,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-313\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +315\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2900,7 +2918,7 @@ Policy Analysis Archives*, *25*(34), 1–27.
-314\. Wang, Y. (2020). Understanding Congressional coalitions: A +316\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30. @@ -2909,7 +2927,7 @@ Student Succeeds act. *Education Policy Analysis Archives*, *28*(119),
-315\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +317\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & Hekkert, M. P. (2024). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. *Environmental Policy and Governance*, *34*(2), 152–165. @@ -2919,7 +2937,7 @@ interest organizations in fragmented socio-technical systems.
-316\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +318\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2928,7 +2946,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-317\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +319\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2938,7 +2956,7 @@ The influence of global and national institutional dynamics.
-318\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using +320\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using collaborative governance to regulate sober living facilities: Structures and strategies for mitigating the influence of powerful actors in multi-sectoral networks. *Administration & Society*, *56*(4), 473–510. @@ -2948,7 +2966,7 @@ multi-sectoral networks. *Administration & Society*, *56*(4), 473–510.
-319\. Wu, J., & Liu, Y. (2020). Deception detection methods +321\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2957,7 +2975,7 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-320\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +322\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948. @@ -2966,7 +2984,7 @@ Information Technology*, *34*(9), 936–948.
-321\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission +323\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. *Higher Education Policy*, *37*, 209–236. @@ -2975,7 +2993,7 @@ policy evolution from 2003 to 2020 in China – a social network analysis.
-322\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +324\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -2984,7 +3002,7 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-323\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +325\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32. @@ -2993,7 +3011,7 @@ infrastructure. *Politics & Policy*, *50*(1), 8–32.
-324\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring +326\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring emotional discourses: The case of COVID-19 protests in the US media. *Administrative Theory & Praxis*, *46*(1), 35–54. @@ -3002,7 +3020,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-325\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +327\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -3011,7 +3029,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-326\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +328\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -3020,7 +3038,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-327\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +329\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), 107–129. @@ -3029,7 +3047,7 @@ transmission line sitings. *Public Policy and Administration*, *38*(1),
-328\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +330\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. From 457c47ec13be5ad04ba72c0f70a1e518864310d1 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 22 Jul 2024 15:57:58 +0200 Subject: [PATCH 17/43] Revised phase transition code --- dna/src/main/java/dna/Dna.java | 2 +- dna/src/main/java/dna/HeadlessDna.java | 128 ++++++ dna/src/main/java/export/Exporter.java | 20 +- rDNA/rDNA/DESCRIPTION | 3 +- rDNA/rDNA/NAMESPACE | 1 - rDNA/rDNA/R/rDNA.R | 577 ++++--------------------- rDNA/rDNA/man/dna_phaseTransitions.Rd | 192 ++++---- 7 files changed, 294 insertions(+), 629 deletions(-) diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index d132e8ea..ac7b2078 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-07-16"; + public static final String date = "2024-07-22"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/dna/src/main/java/dna/HeadlessDna.java b/dna/src/main/java/dna/HeadlessDna.java index 3131b29b..dfe93bce 100644 --- a/dna/src/main/java/dna/HeadlessDna.java +++ b/dna/src/main/java/dna/HeadlessDna.java @@ -469,6 +469,134 @@ public void rNetwork(String networkType, String statementType, String variable1, } } + /** + * Compute a sequence of one-mode or two-mode network matrices using time windows based on R arguments. + * + * @param networkType The network type as provided by rDNA (can be {@code "eventlist"}, {@code "twomode"}, or {@code "onemode"}). + * @param statementType Statement type as a {@link String}. + * @param variable1 First variable for export, provided as a {@link String}. + * @param variable1Document boolean indicating if the first variable is at the document level. + * @param variable2 Second variable for export, provided as a {@link String}. + * @param variable2Document boolean indicating if the second variable is at the document level. + * @param qualifier Qualifier variable as a {@link String}. + * @param qualifierDocument boolean indicating if the qualifier variable is at the document level. + * @param qualifierAggregation Aggregation rule for the qualifier variable (can be {@code "ignore"}, {@code "combine"}, {@code "subtract"}, {@code "congruence"}, or {@code "conflict"}). + * @param normalization Normalization setting as a {@link String}, as provided by rDNA (can be {@code "no"}, {@code "activity"}, {@code "prominence"}, {@code "average"}, {@code "jaccard"}, or {@code "cosine"}). + * @param includeIsolates boolean indicating whether nodes not currently present should still be inserted into the network matrix. + * @param duplicates An input {@link String} from rDNA that can be {@code "include"}, {@code "document"}, {@code "week"}, {@code "month"}, {@code "year"}, or {@code "acrossrange"}. + * @param startDate Start date for the export, provided as a {@link String} with format {@code "dd.MM.yyyy"}. + * @param stopDate Stop date for the export, provided as a {@link String} with format {@code "dd.MM.yyyy"}. + * @param startTime Start time for the export, provided as a {@link String} with format {@code "HH:mm:ss"}. + * @param stopTime Stop time for the export, provided as a {@link String} with format {@code "HH:mm:ss"}. + * @param timeWindow A {@link String} indicating the time window setting. Valid options are {@code "no"}, {@code "events"}, {@code "seconds"}, {@code "minutes"}, {@code "hours"}, {@code "days"}, {@code "weeks"}, {@code "months"}, and {@code "years"}. + * @param windowSize Duration of the time window in the units specified in the {@code timeWindow} argument. + * @param kernel The kernel function for temporal smoothing: {@code "no"}, {@code "uniform"}, {@code "epanechnikov"}, {@code "triangular"}, or {@code "gaussian"}. + * @param normalizeToOne boolean indicating if each cell in the network matrix should be divided by the sum of all cells of the same time step (i.e., normalized to one such that the sum of all cells is {@code 1.0}). + * @param indentTime boolean indicating if the timeline should be indented at the beginning and end such that all networks in the sequence of time windows must fit entirely into the timeline. If false, the first mid-point starts at the beginning, and the last network ends at the stop date and time. + * @param excludeVariables A {@link String} array with n elements, indicating the variable of the n'th value. + * @param excludeValues A {@link String} array with n elements, indicating the value pertaining to the n'th variable {@link String}. + * @param excludeAuthors A {@link String} array of values to exclude in the {@code author} variable at the document level. + * @param excludeSources A {@link String} array of values to exclude in the {@code source} variable at the document level. + * @param excludeSections A {@link String} array of values to exclude in the {@code section} variable at the document level. + * @param excludeTypes A {@link String} array of values to exclude in the {@code "type"} variable at the document level. + * @param invertValues boolean indicating whether the statement-level exclude values should be included (= {@code true}) rather than excluded. + * @param invertAuthors boolean indicating whether the document-level author values should be included (= {@code true}) rather than excluded. + * @param invertSources boolean indicating whether the document-level source values should be included (= {@code true}) rather than excluded. + * @param invertSections boolean indicating whether the document-level section values should be included (= {@code true}) rather than excluded. + * @param invertTypes boolean indicating whether the document-level type values should be included (= {@code true}) rather than excluded. + * @return A {@link Matrix} object containing the resulting one-mode or two-mode network. + */ + public void rTimeWindow(String networkType, String statementType, String variable1, boolean variable1Document, String variable2, + boolean variable2Document, String qualifier, boolean qualifierDocument, String qualifierAggregation, String normalization, boolean includeIsolates, + String duplicates, String startDate, String stopDate, String startTime, String stopTime, String timeWindow, int windowSize, String kernel, + boolean normalizeToOne, boolean indentTime, String[] excludeVariables, String[] excludeValues, String[] excludeAuthors, String[] excludeSources, + String[] excludeSections, String[] excludeTypes, boolean invertValues, boolean invertAuthors, boolean invertSources, boolean invertSections, + boolean invertTypes) { + + // step 1: preprocess arguments + StatementType st = Dna.sql.getStatementType(statementType); // format statement type + + // format dates and times with input formats "dd.MM.yyyy" and "HH:mm:ss" + LocalDateTime ldtStart, ldtStop; + LocalDateTime[] dateRange = formatDateTime(startDate, startTime, stopDate, stopTime); + ldtStart = dateRange[0]; + ldtStop = dateRange[1]; + + // process exclude variables: create HashMap with variable:value pairs + HashMap> map = new HashMap>(); + if (excludeVariables.length > 0) { + for (int i = 0; i < excludeVariables.length; i++) { + ArrayList values = map.get(excludeVariables[i]); + if (values == null) { + values = new ArrayList(); + } + if (!values.contains(excludeValues[i])) { + values.add(excludeValues[i]); + } + Collections.sort(values); + map.put(excludeVariables[i], values); + } + } + + // initialize Exporter class + this.exporter = new Exporter( + networkType, + st, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + includeIsolates, + duplicates, + ldtStart, + ldtStop, + timeWindow, + windowSize, + map, + Stream.of(excludeAuthors).collect(Collectors.toCollection(ArrayList::new)), + Stream.of(excludeSources).collect(Collectors.toCollection(ArrayList::new)), + Stream.of(excludeSections).collect(Collectors.toCollection(ArrayList::new)), + Stream.of(excludeTypes).collect(Collectors.toCollection(ArrayList::new)), + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes, + null, + null); + this.exporter.setKernelFunction(kernel); + this.exporter.setIndentTime(indentTime); + + // step 2: filter + this.exporter.loadData(); + this.exporter.filterStatements(); + + // step 3: compute results + if (networkType.equals("eventlist")) { + // TODO + } else { + try { + this.exporter.computeResults(); + + // step 4: normalize to one + if (normalizeToOne) { + this.exporter.normalizeMatrixResultsToOne(); + } + } catch (Exception e) { + LogEvent le = new LogEvent(Logger.ERROR, + "Error while exporting network.", + "An unexpected error occurred while exporting a network. See the stack trace for details. Consider reporting this error.", + e); + Dna.logger.log(le); + } + } + + } + /** * Generate data to construct a barplot. * diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index 47b3f885..c0a0c15a 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -121,14 +121,21 @@ public class Exporter { * start date and the last mid-point be half a bandwidth or duration before the last date to allow sufficient data * around the end points of the timeline? */ - private boolean indentBandwidth = true; + private boolean indentTime = true; public void setKernelFunction(String kernel) { this.kernel = kernel; } - public void setIndentBandwidth(boolean indentBandwidth) { - this.indentBandwidth = indentBandwidth; + + /** + * Set the value of the indentBandwidth field in this class. It indicates if the start and end date of the time + * window algorithm should be adjusted to include only networks that entirely fit into the set timeline. + * + * @param indentTime Parameter setting. Should the start and stop date and time be indented? + */ + public void setIndentTime(boolean indentTime) { + this.indentTime = indentTime; } /** @@ -1738,7 +1745,7 @@ public void computeKernelSmoothedTimeSlices() { LocalDateTime e = this.stopDateTime.isAfter(lastDate) ? lastDate : this.stopDateTime; // end of statement list LocalDateTime gamma = b; // current time while progressing through list of statements LocalDateTime e2 = e; // indented end point (e minus half w) - if (Exporter.this.indentBandwidth) { + if (Exporter.this.indentTime) { if (timeWindow.equals("minutes")) { gamma = gamma.plusMinutes(W_HALF); e2 = e.minusMinutes(W_HALF); @@ -2091,9 +2098,10 @@ private double zeta(LocalDateTime t, LocalDateTime gamma, int w, String timeWind } /** - * Normalize all values in each results matrix to make them sum to 1.0. Useful for phase transition methods. + * Normalize all values in each results matrix to make them sum to 1.0. Useful for phase transition methods. Called + * directly from R. */ - public void normalizeMatrixResults() { + public void normalizeMatrixResultsToOne() { try (ProgressBar pb = new ProgressBar("Matrix normalization", Exporter.this.matrixResults.size())) { for (Matrix matrixResult : Exporter.this.matrixResults) { double[][] matrix = matrixResult.getMatrix(); diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index ecf45097..994b128a 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-07-16 +Date: 2024-07-22 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", @@ -41,7 +41,6 @@ Suggests: heatmaply (>= 1.4.2) SystemRequirements: Java (>= 11) License: GPL-2 -LazyData: true URL: https://github.com/leifeld/dna BugReports: https://github.com/leifeld/dna/issues Encoding: UTF-8 diff --git a/rDNA/rDNA/NAMESPACE b/rDNA/rDNA/NAMESPACE index a449fcf4..440cc15c 100644 --- a/rDNA/rDNA/NAMESPACE +++ b/rDNA/rDNA/NAMESPACE @@ -29,7 +29,6 @@ export(dna_network) export(dna_openConnectionProfile) export(dna_openDatabase) export(dna_phaseTransitions) -export(dna_phaseTransitions2) export(dna_printDetails) export(dna_queryCoders) export(dna_sample) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index a1ba7683..ae15acd4 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -4342,13 +4342,10 @@ print.dna_multiclust <- function(x, ...) { #' be applied to identify the different stages and phases from the resulting #' distance matrix. #' -#' In contrast to the \code{\link{dna_phaseTransitions}} function, the -#' \code{\link{dna_phaseTransitions2}} function does not offer split nodes. -#' However, it offers two advantages. The first one is faster computation and -#' better parallelization in Java. The second one is kernel smoothing, which -#' means the farther away from a time point a statement is, the less important -#' it becomes for the network that is created around the time point. Several -#' kernel smoothing functions are available; see the \code{kernel} argument. +#' The function offers kernel smoothing, which means the farther away from a +#' time point a statement is, the less important it becomes for the network that +#' is created around the time point. Several kernel smoothing functions are +#' available; see the \code{kernel} argument. #' #' @param distanceMethod The distance measure that expresses the dissimilarity #' between any two network matrices. The following choices are available: @@ -4363,13 +4360,7 @@ print.dna_multiclust <- function(x, ...) { #' distance between two network matrices. Any negative values (e.g., from #' the subtract method) are replaced by zero before computing the #' distance. -#' \item \code{"modularity"}: The difference in maximal modularity as -#' obtained through an approximation via community detection. Note that -#' this method has not been implemented. #' } -#' @param normalizeNetwork Divide all cells by their sum before computing -#' the dissimilarity between two network matrices? This normalization scales -#' all edge weights to a sum of \code{1.0}. #' @param clusterMethods The clustering techniques that are applied to the #' distance matrix in the end. Hierarchical methods are repeatedly cut off at #' different levels, and solutions are compared using network modularity to @@ -4424,87 +4415,89 @@ print.dna_multiclust <- function(x, ...) { #' @param k.max For the hierarchical cluster methods, up to how many clusters or #' states should be identified? Only the best solution between \code{k.min} #' and \code{k.max} clusters is retained and compared to other methods. -#' @param splitNodes Split the nodes of a one-mode network (e.g., concepts in a -#' concept x concept congruence network) into multiple separate nodes, one for -#' each qualifier level? For example, if the qualifier variable is the boolean -#' \code{"agreement"} variable with values \code{0} or \code{1}, then each -#' concept is included twice, one time with suffix \code{"- 0"} and one time -#' with suffix \code{"- 1"}. This is useful when the nodes do not possess -#' agency and positive and negative levels of the variable should not be -#' connected through congruence ties. It helps preserve the signed nature of -#' the data in this case. Do not use with actor networks! #' @param cores The number of computing cores for parallel processing. If #' \code{1} (the default), no parallel processing is used. If a larger number, -#' the \pkg{pbmcapply} package is used to parallelize the computation of the -#' distance matrix and the clustering. Note that this method is based on -#' forking and is only available on Unix operating systems, including MacOS -#' and Linux. In the \code{dna_phaseTransitions2} function, only the -#' clustering is done using this parallelization in R while the remaining -#' computations are done in parallel using threads in Java, which is more -#' efficient. -#' @param kernel Use kernel smoothing for computing network time slices? The -#' default value \code{kernel = "no"} switches off kernel smoothing, which -#' means all statements within a time window are weighted equally. Other -#' values down-weight statements the farther they are temporally away from the -#' temporal mid-point of the respective time slice. Several kernel smoothing -#' functions are available, similar to kernel density estimation: -#' \code{"uniform"} is similar to \code{"no"} and weights all statements with -#' a value of \code{0.5}. \code{"gaussian"} uses a standard normal -#' distribution as a kernel smoother. \code{"epanechnikov"} uses an -#' Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel -#' function. +#' the \pkg{pbmcapply} package is used to parallelize the clustering. Note +#' that this method is based on forking and is only available on Unix +#' operating systems, including MacOS and Linux. Note also that the remaining +#' computations, including the computation of the distance matrix and the +#' time window network generation with kernel smoothing, are done in parallel +#' using threads in Java, irrespective of this setting, using as many parallel +#' threads as cores are available on the system. +#' @param kernel Use kernel smoothing for computing network time slices? Several +#' kernel smoothing functions are available, similar to kernel density +#' estimation. They down-weight statements the farther they are temporally +#' away from the temporal mid-point of the respective time slice. Valid +#' settings are: +#' \itemize{ +#' \item \code{"uniform"}: Weight all statements within a time window +#' equally with a value of \code{0.5}. +#' \item \code{"triangular"}: Use a triangular kernel function. +#' \item \code{"epanechnikov"}: Use an Epanechnikov kernel smoother. +#' \item \code{"gaussian"}: Use a standard normal distribution as a kernel +#' smoother. +#' \item \code{"no"}: Circumvent kernel smoothing and weight all statements +#' with a value of \code{1.0}. This is a legacy setting and is slow and +#' may not return the same results as \code{"uniform"} due to the way it +#' was written up. +#' } +#' @param normalizeToOne Divide all cells by the sum of all cells before +#' computing the dissimilarity between two network matrices? This +#' normalization scales all edge weights to a sum of \code{1.0}. Doing so can +#' make networks more comparable by boosting the edge weights of networks that +#' are relatively sparsely populated by concepts, for example at the beginning +#' or end of the debate. Note that this normalization should not make any +#' difference with Euclidean spectral distances of the graph Laplacian because +#' the eigenvalues are normalized to sum to one in this distance method. +#' @param indentTime If \code{TRUE}, the sequence of time slices under the time +#' window algorithm starts with the first network and ends with the last +#' network that are entirely covered within the timeline defined by the start +#' and stop dates and times. For example, if the start date is 1 February, the +#' stop date is 31 December, and the time window duration is 21 days, the +#' mid-point of the first time window will be 11 February (to ensure the first +#' network entirely fits into the timeline), and the last network will be +#' centered around 20 December (to ensure the last network entirely fits into +#' the timeline). If \code{FALSE}, the start and stop dates and times are used +#' as the first and last mid-points. In that case, the first and last few +#' networks may contain fewer statements than other time slices and may, +#' therefore, be more similar to each other. This can potentially be +#' counter-acted by setting the \code{normalizeToOne} argument. #' @inheritParams dna_network #' #' @examples #' \dontrun{ +#' library("ggplot2") #' dna_init() #' dna_sample() #' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") #' #' # compute states and phases for sample dataset -#' results <- dna_phaseTransitions(distanceMethod = "absdiff", -#' clusterMethods = c("ward", -#' "pam", -#' "concor", -#' "walktrap"), -#' cores = 1, -#' k.min = 2, -#' k.max = 6, -#' networkType = "onemode", -#' variable1 = "organization", -#' variable2 = "concept", -#' timeWindow = "events", -#' windowSize = 15) -#' results -#' -#' # kernel smoothing and spectral distances using dna_phaseTransitions2 -#' results2 <- dna_phaseTransitions2(distanceMethod = "spectral", -#' clusterMethods = c("ward", -#' "pam", -#' "concor", -#' "walktrap"), -#' k.min = 2, -#' k.max = 6, -#' networkType = "onemode", -#' variable1 = "organization", -#' variable2 = "concept", -#' timeWindow = "days", -#' windowSize = 15, -#' kernel = "gaussian") +#' results2 <- dna_phaseTransitions(distanceMethod = "spectral", +#' clusterMethods = c("ward", +#' "pam", +#' "concor", +#' "walktrap"), +#' k.min = 2, +#' k.max = 6, +#' networkType = "onemode", +#' variable1 = "organization", +#' variable2 = "concept", +#' timeWindow = "days", +#' windowSize = 15, +#' kernel = "gaussian", +#' indentTime = FALSE, +#' normalizeToOne = FALSE) #' results2 -#' -#' library("ggplot2") #' autoplot(results2) #' } #' -#' @author Philip Leifeld, Kristijan Garic -#' #' @rdname dna_phaseTransitions +#' @author Philip Leifeld #' @importFrom stats dist #' @importFrom utils combn +#' @importFrom rJava .jarray .jcall .jnull J #' @export dna_phaseTransitions <- function(distanceMethod = "absdiff", - normalizeNetwork = FALSE, clusterMethods = c("single", "average", "complete", @@ -4516,7 +4509,6 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", "walktrap"), k.min = 2, k.max = 6, - splitNodes = FALSE, cores = 1, networkType = "twomode", statementType = "DNA Statement", @@ -4534,7 +4526,10 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", start.time = "00:00:00", stop.time = "23:59:59", timeWindow = "days", - windowSize = 150, + windowSize = 200, + kernel = "uniform", + normalizeToOne = FALSE, + indentTime = TRUE, excludeValues = list(), excludeAuthors = character(), excludeSources = character(), @@ -4571,417 +4566,6 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", clusterMethods <- rev(clusterMethods) # reverse order to save time during parallel computation by starting the computationally intensive methods first mcall <- match.call() # save the arguments for storing them in the results later - # generate the time window networks - if (is.null(timeWindow) || is.na(timeWindow) || !is.character(timeWindow) || length(timeWindow) != 1 || !timeWindow %in% c("events", "seconds", "minutes", "hours", "days", "weeks", "months", "years")) { - timeWindow <- "events" - warning("The 'timeWindow' argument was invalid. Proceeding with 'timeWindow = \"events\" instead.") - } - if (qualifierAggregation == "split") { # splitting first-mode nodes by qualifier - if (networkType != "onemode") { - stop("The 'qualifierAggregation' argument accepts the value \"split\" only in conjunction with creating one-mode networks.") - } - v <- dna_getVariables(statementType) - if (v$type[v$label == qualifier] != "boolean") { - stop("The 'qualifierAggregation = \"split\" argument currently only works with boolean qualifier variables.") - } - aff <- dna_network(networkType = "twomode", - statementType = statementType, - variable1 = variable1, - variable1Document = variable1Document, - variable2 = variable2, - variable2Document = variable2Document, - qualifier = qualifier, - qualifierDocument = qualifierDocument, - qualifierAggregation = "combine", - normalization = "no", - isolates = TRUE, - duplicates = duplicates, - start.date = start.date, - stop.date = stop.date, - start.time = start.time, - stop.time = stop.time, - timeWindow = timeWindow, - windowSize = windowSize, - kernel = "no", - excludeValues = excludeValues, - excludeAuthors = excludeAuthors, - excludeSources = excludeSources, - excludeSections = excludeSections, - excludeTypes = excludeTypes, - invertValues = invertValues, - invertAuthors = invertAuthors, - invertSources = invertSources, - invertSections = invertSections, - invertTypes = invertTypes, - fileFormat = NULL, - outfile = NULL) - cat("Splitting nodes... ") - nw <- lapply(aff, function(x) { - pos <- x - pos[pos == 2] <- 0 - rownames(pos) <- paste(rownames(pos), "- 1") - neg <- x - neg[neg == 1] <- 0 - rownames(neg) <- paste(rownames(neg), "- 0") - combined <- rbind(pos, neg) - congruence <- combined %*% t(combined) - diag(congruence) <- 0 - rs <- x - rs[rs != 0] <- 1 - rs <- rowSums(rs) - if (normalization == "average") { - denominator <- matrix(1, nrow = nrow(congruence), ncol = ncol(congruence)) - for (i in 1:nrow(x)) { - for (j in 1:ncol(x)) { - d <- (rs[i] + rs[j]) / 2 - if (d != 0) denominator[i, j] <- d - } - } - result <- congruence / denominator - } else if (normalization == "jaccard") { - unions <- matrix(rep(rs, times = length(rs)), nrow = length(rs)) + t(matrix(rep(rs, times = length(rs)), nrow = length(rs))) - congruence - result <- congruence / unions - diag(result) <- 0 - } else if (normalization == "cosine") { - magnitudes <- sqrt(rs) - result <- congruence / (outer(magnitudes, magnitudes)) - diag(result) <- 1 - } else if (normalization == "no") { - result <- congruence - diag(result) <- 0 - } else { - normalization <- "no" - result <- congruence - diag(result) <- 0 - warning("Invalid normalization setting for one-mode networks. Switching off normalization.") - } - class(result) <- c("dna_network_onemode", class(result)) - attributes(result)$start <- attributes(x)$start - attributes(result)$stop <- attributes(x)$stop - attributes(result)$middle <- attributes(x)$middle - attributes(result)$numStatements <- attributes(x)$numStatements - attributes(result)$call <- mcall - return(result) - }) - cat(intToUtf8(0x2714), "\n") - } else { # letting dna_network create the networks - nw <- dna_network(networkType = networkType, - statementType = statementType, - variable1 = variable1, - variable1Document = variable1Document, - variable2 = variable2, - variable2Document = variable2Document, - qualifier = qualifier, - qualifierDocument = qualifierDocument, - qualifierAggregation = qualifierAggregation, - normalization = normalization, - isolates = TRUE, - duplicates = duplicates, - start.date = start.date, - stop.date = stop.date, - start.time = start.time, - stop.time = stop.time, - timeWindow = timeWindow, - windowSize = windowSize, - kernel = "no", - excludeValues = excludeValues, - excludeAuthors = excludeAuthors, - excludeSources = excludeSources, - excludeSections = excludeSections, - excludeTypes = excludeTypes, - invertValues = invertValues, - invertAuthors = invertAuthors, - invertSources = invertSources, - invertSections = invertSections, - invertTypes = invertTypes, - fileFormat = NULL, - outfile = NULL) - } - - # normalize network matrices to sum to 1.0 - if (normalizeNetwork) { - cat("Normalizing network to sum to 1... ") - nw <- lapply(nw, function(x) { - s <- sum(x) - ifelse(s == 0, return(x), return(x / s)) - }) - cat(intToUtf8(0x2714), "\n") - } - - # define distance function for network comparison - if (distanceMethod == "absdiff") { - d <- function(pair, data) { - sum(abs(data[[pair[1]]] - data[[pair[2]]])) - } - } else if (distanceMethod == "spectral") { - d <- function(pair, data) { - data[[pair[1]]][data[[pair[1]]] < 0] <- 0 # replace negative values by zero - data[[pair[2]]][data[[pair[2]]] < 0] <- 0 # for example, in a subtract network - eigen_x <- eigen(diag(rowSums(data[[pair[1]]])) - data[[pair[1]]]) # eigenvalues for each Laplacian matrix - eigen_y <- eigen(diag(rowSums(data[[pair[2]]])) - data[[pair[2]]]) - eigen_x <- eigen_x$values / sum(eigen_x$values) # normalize to sum to 1.0 - eigen_y <- eigen_y$values / sum(eigen_y$values) - return(sqrt(sum((eigen_x - eigen_y)^2))) # return square root of the sum of squared differences (Euclidean distance) between the normalized eigenvalues - } - } else if (distanceMethod == "modularity") { - stop("Differences in modularity have not been implemented yet. Please use absolute differences or spectral Euclidean distance as a distance method.") - } else { - stop("Distance method not recognized. Try \"absdiff\" or \"spectral\".") - } - - # apply distance function and create distance matrix - distance_mat <- matrix(0, nrow = length(nw), ncol = length(nw)) - pairs <- combn(length(nw), 2, simplify = FALSE) - if (cores > 1) { - cat(paste("Computing distances on", cores, "cores.\n")) - a <- Sys.time() - distances <- pbmcapply::pbmclapply(pairs, d, data = nw, mc.cores = cores) - b <- Sys.time() - } else { - cat("Computing distances... ") - a <- Sys.time() - distances <- lapply(pairs, d, data = nw) - b <- Sys.time() - cat(intToUtf8(0x2714), "\n") - } - distance_mat[lower.tri(distance_mat)] <- unlist(distances) - distance_mat <- distance_mat + t(distance_mat) - distance_mat[is.nan(distance_mat)] <- 0 # replace NaN values with zeros - # distance_mat <- distance_mat + 1e-12 # adding small constant - distance_mat <- distance_mat / max(distance_mat) # rescale between 0 and 1 - print(b - a) - - # define clustering function - hclustMethods <- c("single", "average", "complete", "ward") - cl <- function(method, distmat) { - tryCatch({ - similarity_mat <- 1 - distmat - g <- igraph::graph.adjacency(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance - if (method %in% hclustMethods) { - if (method == "single") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "single")) - } else if (method == "average") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "average")) - } else if (method == "complete") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "complete")) - } else if (method == "ward") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "ward.D2")) - } - opt_k <- lapply(k.min:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "kmeans") { - opt_k <- lapply(k.min:k.max, function(x) { - suppressWarnings(cl <- stats::kmeans(distmat, centers = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "pam") { - opt_k <- lapply(k.min:k.max, function(x) { - suppressWarnings(cl <- cluster::pam(distmat, k = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "spectral") { - sigma <- 1.0 - affinity_matrix <- exp(-distmat^2 / (2 * sigma^2)) - L <- diag(rowSums(affinity_matrix)) - affinity_matrix - D.sqrt.inv <- diag(1 / sqrt(rowSums(affinity_matrix))) - L.norm <- D.sqrt.inv %*% L %*% D.sqrt.inv - eigenvalues <- eigen(L.norm) # eigenvalue decomposition - opt_k <- lapply(k.min:k.max, function(x) { - U <- eigenvalues$vectors[, 1:x] - mem <- kmeans(U, centers = x)$cluster # cluster the eigenvectors - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "concor") { - suppressWarnings(mi <- stats::cor(similarity_mat)) - iter <- 1 - while (any(abs(mi) <= 0.999) & iter <= 50) { - mi[is.na(mi)] <- 0 - mi <- stats::cor(mi) - iter <- iter + 1 - } - mem <- ((mi[, 1] > 0) * 1) + 1 - } else if (method %in% igraphMethods) { - if (method == "fastgreedy") { - suppressWarnings(cl <- igraph::cluster_fast_greedy(g)) - } else if (method == "walktrap") { - suppressWarnings(cl <- igraph::cluster_walktrap(g)) - } else if (method == "leading_eigen") { - suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) - } else if (method == "edge_betweenness") { - suppressWarnings(cl <- igraph::cluster_edge_betweenness(g)) - } else if (method == "spinglass") { - suppressWarnings(cl <- igraph::cluster_spinglass(g)) - } - opt_k <- lapply(k.min:k.max, function(x) { - mem <- igraph::cut_at(communities = cl, no = x) - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } - list(method = method, - modularity = igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem), - memberships = mem) - }, - error = function(e) { - warning("Cluster method '", method, "' could not be computed due to an error: ", e) - }, - warning = function(w) { - warning("Cluster method '", method, "' threw a warning: ", w) - }) - } - - # apply all clustering methods to distance matrix - if (cores > 1) { - cat(paste("Clustering distance matrix on", cores, "cores.\n")) - a <- Sys.time() - l <- pbmcapply::pbmclapply(clusterMethods, cl, distmat = distance_mat, mc.cores = cores) - b <- Sys.time() - } else { - cat("Clustering distance matrix... ") - a <- Sys.time() - l <- lapply(clusterMethods, cl, distmat = distance_mat) - b <- Sys.time() - cat(intToUtf8(0x2714), "\n") - } - print(b - a) - for (i in length(l):1) { - if (length(l[[i]]) == 1) { - l <- l[-i] - clusterMethods <- clusterMethods[-i] - } - } - results <- list() - mod <- sapply(l, function(x) x$modularity) - best <- which(mod == max(mod))[1] - results$modularity <- mod[best] - results$clusterMethod <- clusterMethods[best] - dates <- sapply(nw, function(x) attributes(x)$middle) - - # temporal embedding via MDS - if (!requireNamespace("MASS", quietly = TRUE)) { - mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), - "state" = l[[best]]$memberships) - results$states <- mem - warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") - } else { - cat("Temporal embedding...\n") - a <- Sys.time() - distmat <- distance_mat + 1e-12 - mds <- MASS::isoMDS(distmat) # MDS of distance matrix - points <- mds$points - mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), - "state" = l[[best]]$memberships, - "X1" = points[, 1], - "X2" = points[, 2]) - results$states <- mem - b <- Sys.time() - print(b - a) - } - - results$distmat <- distance_mat - class(results) <- "dna_phaseTransitions" - attributes(results)$stress <- ifelse(ncol(results$states) == 2, NA, mds$stress) - attributes(results)$call <- mcall - return(results) -} - -#' @rdname dna_phaseTransitions -#' @author Philip Leifeld -#' @importFrom stats dist -#' @importFrom utils combn -#' @importFrom rJava .jarray .jcall .jnull J -#' @export -dna_phaseTransitions2 <- function(distanceMethod = "absdiff", - normalizeNetwork = FALSE, - clusterMethods = c("single", - "average", - "complete", - "ward", - "kmeans", - "pam", - "spectral", - "fastgreedy", - "walktrap"), - k.min = 2, - k.max = 6, - cores = 1, - networkType = "twomode", - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "subtract", - normalization = "no", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - timeWindow = "days", - windowSize = 150, - kernel = "no", - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE) { - - # check arguments and packages - if (distanceMethod == "spectral" && networkType == "twomode") { - distanceMethod <- "absdiff" - warning("Spectral distances only work with one-mode networks. Using 'distanceMethod = \"absdiff\"' instead.") - } - if (cores > 1 && !requireNamespace("pbmcapply", quietly = TRUE)) { - pbmclapply <- FALSE - warning("Argument 'cores' requires the 'pbmcapply' package, which is not installed.\nSetting 'cores = 1'. Consider installing the 'pbmcapply' package if you use Linux or MacOS.") - } - igraphMethods <- c("louvain", "fastgreedy", "walktrap", "leading_eigen", "edge_betweenness", "infomap", "label_prop", "spinglass") - if (any(igraphMethods %in% clusterMethods) && !requireNamespace("igraph", quietly = TRUE)) { - clusterMethods <- clusterMethods[-igraphMethods] - warning("'igraph' package not installed. Dropping clustering methods from the 'igraph' package. Consider installing 'igraph'.") - } - if ("pam" %in% clusterMethods && !requireNamespace("cluster", quietly = TRUE)) { - clusterMethods <- clusterMethods[which(clusterMethods != "pam")] - warning("'cluster' package not installed. Dropping clustering methods from the 'cluster' package. Consider installing 'cluster'.") - } - if ("concor" %in% clusterMethods && k.min > 2) { - clusterMethods <- clusterMethods[which(clusterMethods != "concor")] - warning("Dropping 'concor' from clustering methods because the CONCOR implementation in rDNA can only find exactly two clusters, but the 'k.min' argument was larger than 2.") - } - clusterMethods <- rev(clusterMethods) # reverse order to save time during parallel computation by starting the computationally intensive methods first - mcall <- match.call() # save the arguments for storing them in the results later - # generate the time window networks if (is.null(timeWindow) || is.na(timeWindow) || !is.character(timeWindow) || length(timeWindow) != 1 || !timeWindow %in% c("events", "seconds", "minutes", "hours", "days", "weeks", "months", "years")) { timeWindow <- "events" @@ -5020,13 +4604,11 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", if (is.null(qualifier) || is.na(qualifier)) { qualifier <- .jnull(class = "java/lang/String") } - fileFormat <- .jnull(class = "java/lang/String") - outfile <- .jnull(class = "java/lang/String") # call rNetwork function to compute results .jcall(dna_getHeadlessDna(), "V", - "rNetwork", + "rTimeWindow", networkType, statementType, variable1, @@ -5046,6 +4628,8 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", timeWindow, as.integer(windowSize), kernel, + normalizeToOne, + indentTime, var, val, excludeAuthors, @@ -5056,19 +4640,10 @@ dna_phaseTransitions2 <- function(distanceMethod = "absdiff", invertAuthors, invertSources, invertSections, - invertTypes, - outfile, - fileFormat + invertTypes ) exporter <- dna_getHeadlessDna()$getExporter() # save Java object reference to exporter class - # matrix normalization - if (normalizeNetwork) { - .jcall(exporter, - "V", - "normalizeMatrixResults") - } - # compute distance matrix if (distanceMethod == "modularity") { stop("Differences in modularity have not been implemented yet. Please use absolute differences or spectral Euclidean distance as a distance method.") @@ -5383,4 +4958,4 @@ autoplot.dna_phaseTransitions <- function(object, ..., plots = c("heatmap", "sil } return(l) -} +} \ No newline at end of file diff --git a/rDNA/rDNA/man/dna_phaseTransitions.Rd b/rDNA/rDNA/man/dna_phaseTransitions.Rd index 2fcd8b96..ce9b8c23 100644 --- a/rDNA/rDNA/man/dna_phaseTransitions.Rd +++ b/rDNA/rDNA/man/dna_phaseTransitions.Rd @@ -2,19 +2,16 @@ % Please edit documentation in R/rDNA.R \name{dna_phaseTransitions} \alias{dna_phaseTransitions} -\alias{dna_phaseTransitions2} \alias{print.dna_phaseTransitions} \alias{autoplot.dna_phaseTransitions} \title{Detect phase transitions and states in a discourse network} \usage{ dna_phaseTransitions( distanceMethod = "absdiff", - normalizeNetwork = FALSE, clusterMethods = c("single", "average", "complete", "ward", "kmeans", "pam", "spectral", "fastgreedy", "walktrap"), k.min = 2, k.max = 6, - splitNodes = FALSE, cores = 1, networkType = "twomode", statementType = "DNA Statement", @@ -32,45 +29,10 @@ dna_phaseTransitions( start.time = "00:00:00", stop.time = "23:59:59", timeWindow = "days", - windowSize = 150, - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE -) - -dna_phaseTransitions2( - distanceMethod = "absdiff", - normalizeNetwork = FALSE, - clusterMethods = c("single", "average", "complete", "ward", "kmeans", "pam", - "spectral", "fastgreedy", "walktrap"), - k.min = 2, - k.max = 6, - cores = 1, - networkType = "twomode", - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "subtract", - normalization = "no", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - timeWindow = "days", - windowSize = 150, - kernel = "no", + windowSize = 200, + kernel = "uniform", + normalizeToOne = FALSE, + indentTime = TRUE, excludeValues = list(), excludeAuthors = character(), excludeSources = character(), @@ -101,15 +63,8 @@ between any two network matrices. The following choices are available: distance between two network matrices. Any negative values (e.g., from the subtract method) are replaced by zero before computing the distance. - \item \code{"modularity"}: The difference in maximal modularity as - obtained through an approximation via community detection. Note that - this method has not been implemented. }} -\item{normalizeNetwork}{Divide all cells by their sum before computing -the dissimilarity between two network matrices? This normalization scales -all edge weights to a sum of \code{1.0}.} - \item{clusterMethods}{The clustering techniques that are applied to the distance matrix in the end. Hierarchical methods are repeatedly cut off at different levels, and solutions are compared using network modularity to @@ -167,25 +122,15 @@ methods.} states should be identified? Only the best solution between \code{k.min} and \code{k.max} clusters is retained and compared to other methods.} -\item{splitNodes}{Split the nodes of a one-mode network (e.g., concepts in a -concept x concept congruence network) into multiple separate nodes, one for -each qualifier level? For example, if the qualifier variable is the boolean -\code{"agreement"} variable with values \code{0} or \code{1}, then each -concept is included twice, one time with suffix \code{"- 0"} and one time -with suffix \code{"- 1"}. This is useful when the nodes do not possess -agency and positive and negative levels of the variable should not be -connected through congruence ties. It helps preserve the signed nature of -the data in this case. Do not use with actor networks!} - \item{cores}{The number of computing cores for parallel processing. If \code{1} (the default), no parallel processing is used. If a larger number, -the \pkg{pbmcapply} package is used to parallelize the computation of the -distance matrix and the clustering. Note that this method is based on -forking and is only available on Unix operating systems, including MacOS -and Linux. In the \code{dna_phaseTransitions2} function, only the -clustering is done using this parallelization in R while the remaining -computations are done in parallel using threads in Java, which is more -efficient.} +the \pkg{pbmcapply} package is used to parallelize the clustering. Note +that this method is based on forking and is only available on Unix +operating systems, including MacOS and Linux. Note also that the remaining +computations, including the computation of the distance matrix and the +time window network generation with kernel smoothing, are done in parallel +using threads in Java, irrespective of this setting, using as many parallel +threads as cores are available on the system.} \item{networkType}{The kind of network to be computed. Can be \code{"twomode"}, \code{"onemode"}, or \code{"eventlist"}.} @@ -325,6 +270,47 @@ event time.} comprised. This can be the number of statement events, the number of days etc., as defined in the \code{"timeWindow"} argument.} +\item{kernel}{Use kernel smoothing for computing network time slices? Several +kernel smoothing functions are available, similar to kernel density +estimation. They down-weight statements the farther they are temporally +away from the temporal mid-point of the respective time slice. Valid +settings are: +\itemize{ + \item \code{"uniform"}: Weight all statements within a time window + equally with a value of \code{0.5}. + \item \code{"triangular"}: Use a triangular kernel function. + \item \code{"epanechnikov"}: Use an Epanechnikov kernel smoother. + \item \code{"gaussian"}: Use a standard normal distribution as a kernel + smoother. + \item \code{"no"}: Circumvent kernel smoothing and weight all statements + with a value of \code{1.0}. This is a legacy setting and is slow and + may not return the same results as \code{"uniform"} due to the way it + was written up. +}} + +\item{normalizeToOne}{Divide all cells by the sum of all cells before +computing the dissimilarity between two network matrices? This +normalization scales all edge weights to a sum of \code{1.0}. Doing so can +make networks more comparable by boosting the edge weights of networks that +are relatively sparsely populated by concepts, for example at the beginning +or end of the debate. Note that this normalization should not make any +difference with Euclidean spectral distances of the graph Laplacian because +the eigenvalues are normalized to sum to one in this distance method.} + +\item{indentTime}{If \code{TRUE}, the sequence of time slices under the time +window algorithm starts with the first network and ends with the last +network that are entirely covered within the timeline defined by the start +and stop dates and times. For example, if the start date is 1 February, the +stop date is 31 December, and the time window duration is 21 days, the +mid-point of the first time window will be 11 February (to ensure the first +network entirely fits into the timeline), and the last network will be +centered around 20 December (to ensure the last network entirely fits into +the timeline). If \code{FALSE}, the start and stop dates and times are used +as the first and last mid-points. In that case, the first and last few +networks may contain fewer statements than other time slices and may, +therefore, be more similar to each other. This can potentially be +counter-acted by setting the \code{normalizeToOne} argument.} + \item{excludeValues}{A list of named character vectors that contains entries which should be excluded during network construction. For example, \code{list(concept = c("A", "B"), organization = c("org A", "org B"))} @@ -381,18 +367,6 @@ construction (\code{invertTypes = FALSE}) or if they should be the only values that should be included during network construction (\code{invertTypes = TRUE}).} -\item{kernel}{Use kernel smoothing for computing network time slices? The -default value \code{kernel = "no"} switches off kernel smoothing, which -means all statements within a time window are weighted equally. Other -values down-weight statements the farther they are temporally away from the -temporal mid-point of the respective time slice. Several kernel smoothing -functions are available, similar to kernel density estimation: -\code{"uniform"} is similar to \code{"no"} and weights all statements with -a value of \code{0.5}. \code{"gaussian"} uses a standard normal -distribution as a kernel smoother. \code{"epanechnikov"} uses an -Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel -function.} - \item{x}{A \code{dna_phaseTransitions} object.} \item{...}{Additional arguments. Currently not in use.} @@ -417,62 +391,44 @@ Euclidean spectral distance are available. Several clustering techniques can be applied to identify the different stages and phases from the resulting distance matrix. -In contrast to the \code{\link{dna_phaseTransitions}} function, the -\code{\link{dna_phaseTransitions2}} function does not offer split nodes. -However, it offers two advantages. The first one is faster computation and -better parallelization in Java. The second one is kernel smoothing, which -means the farther away from a time point a statement is, the less important -it becomes for the network that is created around the time point. Several -kernel smoothing functions are available; see the \code{kernel} argument. +The function offers kernel smoothing, which means the farther away from a +time point a statement is, the less important it becomes for the network that +is created around the time point. Several kernel smoothing functions are +available; see the \code{kernel} argument. Print a summary of a \code{dna_phaseTransitions} object, which can be created using the \link{dna_phaseTransitions} function. } \examples{ \dontrun{ +library("ggplot2") dna_init() dna_sample() dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") # compute states and phases for sample dataset -results <- dna_phaseTransitions(distanceMethod = "absdiff", - clusterMethods = c("ward", - "pam", - "concor", - "walktrap"), - cores = 1, - k.min = 2, - k.max = 6, - networkType = "onemode", - variable1 = "organization", - variable2 = "concept", - timeWindow = "events", - windowSize = 15) -results - -# kernel smoothing and spectral distances using dna_phaseTransitions2 -results2 <- dna_phaseTransitions2(distanceMethod = "spectral", - clusterMethods = c("ward", - "pam", - "concor", - "walktrap"), - k.min = 2, - k.max = 6, - networkType = "onemode", - variable1 = "organization", - variable2 = "concept", - timeWindow = "days", - windowSize = 15, - kernel = "gaussian") +results2 <- dna_phaseTransitions(distanceMethod = "spectral", + clusterMethods = c("ward", + "pam", + "concor", + "walktrap"), + k.min = 2, + k.max = 6, + networkType = "onemode", + variable1 = "organization", + variable2 = "concept", + timeWindow = "days", + windowSize = 15, + kernel = "gaussian", + indentTime = FALSE, + normalizeToOne = FALSE) results2 - -library("ggplot2") autoplot(results2) } } \author{ -Philip Leifeld, Kristijan Garic - Philip Leifeld + +Philip Leifeld, Kristijan Garic } From 58e7cc92284068fa9df1290f19823c201769cef0 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 22 Jul 2024 16:31:27 +0200 Subject: [PATCH 18/43] Updated bibliography --- bibliography/bibliography.bib | 30 ++- build/bibliography.md | 414 ++++++++++++++++++---------------- 2 files changed, 249 insertions(+), 195 deletions(-) diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index e806fad7..a07f43f5 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -1456,6 +1456,15 @@ @article{koop2023animals doi={10.1080/23251042.2023.2216371} } +@article{kuenzler2024exploring, + title={Exploring the Eternal Struggle: The Narrative Policy Framework and Status Quo versus Policy Change}, + author={Kuenzler, Johanna and Vogeler, Colette and Parth, Anne-Marie and Gohl, Titian}, + journal={Policy Sciences}, + year={2024}, + note={Forthcoming}, + doi={10.1007/s11077-024-09537-6} +} + @article{kukkonen2021actors, title={Actors and Justifications in Media Debates on {A}rctic Climate Change in {F}inland and {C}anada: A Network Approach}, author={Kukkonen, Anna and Stoddart, Mark C. J. and Yl{\"a}-Anttila, Tuomas}, @@ -1464,7 +1473,6 @@ @article{kukkonen2021actors number={1}, pages={103--117}, year={2021}, - publisher={SAGE Publications Sage UK: London, England}, doi={10.1177/0001699319890902} } @@ -2841,6 +2849,15 @@ @mastersthesis{stjacques2019us url={https://www.researchgate.net/publication/362156055_Us_and_them_-_Mapping_discourse_coalitions_in_the_EU_Copyright_Directive_debate} } +@article{stoddart2024instagram, + title={Instagram as an Arena of Climate Change Communication and Mobilization: A Discourse Network Analysis of {COP26}}, + author={Stoddart, Mark C. J. and Koop-Monteiro, Yasmin and Tindall, David B.}, + journal={Environmental Communication}, + year={2024}, + note={Forthcoming}, + doi={10.1080/17524032.2024.2377719} +} + @book{stoddart2020industrial, title={Industrial Development and Eco-Tourisms. {C}an Oil Extraction and Nature Conservation Co-Exist?}, author={Stoddart, Mark C. J. and Mattoni, Alice and McLevey, John}, @@ -3532,3 +3549,14 @@ @mastersthesis{zakova2023expertization school={Masaryk University, Faculty of Social Studies}, url={https://is.muni.cz/th/f9wdw/} } + +@article{zunaedi2024jejaring, + title={Jejaring Wacana Cuitan {T}witter pada Kontroversi Konten Pemeliharaan Satwa Liar}, + author={Zunaedi, Zuraida Zein H.}, + journal={Perspektif}, + volume={13}, + number={3}, + pages={756--766}, + year={2024}, + doi={10.31289/perspektif.v13i3.11592} +} diff --git a/build/bibliography.md b/build/bibliography.md index d146f591..a26f1601 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-07-16 +date: 2024-07-22 title: "Discourse Network Analysis: Bibliography" --- @@ -1283,9 +1283,18 @@ Instagram posts. *Environmental Sociology*, *9*(4), 409–426.
+
+ +138\. Kuenzler, J., Vogeler, C., Parth, A.-M., & Gohl, T. (2024). +Exploring the eternal struggle: The narrative policy framework and +status quo versus policy change. *Policy Sciences*. + + +
+
-138\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors +139\. Kukkonen, A., Stoddart, M. C. J., & YlĂ€-Anttila, T. (2021). Actors and justifications in media debates on Arctic climate change in Finland and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117. @@ -1294,7 +1303,7 @@ and Canada: A network approach. *Acta Sociologica*, *64*(1), 103–117.
-139\. Kukkonen, A. (2018). *Discourse networks and justifications of +140\. Kukkonen, A. (2018). *Discourse networks and justifications of climate change policy: News media debates in Canada, the United States, Finland, France, Brazil and India* \[Doctoral Dissertation, University of Helsinki, Faculty of Social Sciences\]. @@ -1304,7 +1313,7 @@ of Helsinki, Faculty of Social Sciences\].
-140\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to +141\. Kukkonen, A., & MalkamĂ€ki, A. (2023). A cultural approach to politicization of science: How the forestry coalition challenged the scientific consensus in the Finnish news media debate on increased logging. *Society & Natural Resources*, *37*(1), 91–112. @@ -1314,7 +1323,7 @@ logging. *Society & Natural Resources*, *37*(1), 91–112.
-141\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy +142\. Kukkonen, A., & YlĂ€-Anttila, T. (2020). The science–policy interface as a discourse network: Finland’s climate change policy 2002–2015. *Politics and Governance*, *8*(2), 200. @@ -1323,7 +1332,7 @@ interface as a discourse network: Finland’s climate change policy
-142\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy +143\. Kukkonen, A., YlĂ€-Anttila, T., & Broadbent, J. (2017). Advocacy coalitions, beliefs and climate change policy in the United States. *Public Administration*, *95*(3), 713–729. @@ -1332,7 +1341,7 @@ coalitions, beliefs and climate change policy in the United States.
-143\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., +144\. Kukkonen, A., YlĂ€-Anttila, T., Swarnakar, P., Broadbent, J., Lahsen, M., & Stoddart, M. C. J. (2018). International organizations, advocacy coalitions, and domestication of global norms: Debates on climate change in Canada, the US, Brazil, and India. *Environmental @@ -1343,7 +1352,7 @@ Science & Policy*, *81*, 54–62.
-144\. Kurniawati, H. O. (2024). Analisis jejaring wacana Rancangan +145\. Kurniawati, H. O. (2024). Analisis jejaring wacana Rancangan Undang-Undang Kesehatan Tahun 2023 dan potensi Judicial Review ke Mahkamah Konstitusi. *UNES Law Review*, *6*(3), 8660–8675. @@ -1352,7 +1361,7 @@ Mahkamah Konstitusi. *UNES Law Review*, *6*(3), 8660–8675.
-145\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., +146\. Lapesa, G., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., Kuhn, J., & PadĂł, S. (2020). DEbateNet-mig15: Tracing the 2015 immigration debate in Germany over time. *Proceedings of the Twelfth Language Resources and Evaluation Conference*, 919–927. @@ -1362,7 +1371,7 @@ Language Resources and Evaluation Conference*, 919–927.
-146\. Laurer, M., & Seidl, T. (2021). Regulating the European +147\. Laurer, M., & Seidl, T. (2021). Regulating the European data-driven economy: A case study on the general data protection regulation. *Policy & Internet*, *13*(2), 257–277. @@ -1371,7 +1380,7 @@ regulation. *Policy & Internet*, *13*(2), 257–277.
-147\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem +148\. Leifeld, P. (2009). Die Untersuchung von Diskursnetzwerken mit dem Discourse Network Analyzer (DNA). In V. Schneider, F. Janning, P. Leifeld, & T. Malang (Eds.), *Politiknetzwerke. Modelle, Anwendungen und Visualisierungen* (pp. 391–404). Springer VS. @@ -1381,7 +1390,7 @@ Visualisierungen* (pp. 391–404). Springer VS.
-148\. Leifeld, P. (2013). Reconceptualizing major policy change in the +149\. Leifeld, P. (2013). Reconceptualizing major policy change in the advocacy coalition framework: A discourse network analysis of German pension politics. *Policy Studies Journal*, *41*(1), 169–198. @@ -1390,7 +1399,7 @@ pension politics. *Policy Studies Journal*, *41*(1), 169–198.
-149\. Leifeld, P. (2014). Polarization of coalitions in an agent-based +150\. Leifeld, P. (2014). Polarization of coalitions in an agent-based model of political discourse. *Computational Social Networks*, *1*(1), 1–22. @@ -1398,7 +1407,7 @@ model of political discourse. *Computational Social Networks*, *1*(1),
-150\. Leifeld, P. (2016). *Policy debates as dynamic networks: German +151\. Leifeld, P. (2016). *Policy debates as dynamic networks: German pension politics and privatization discourse*. Campus. @@ -1406,7 +1415,7 @@ pension politics and privatization discourse*. Campus.
-151\. Leifeld, P. (2017). Discourse network analysis: Policy debates as +152\. Leifeld, P. (2017). Discourse network analysis: Policy debates as dynamic networks. In J. N. Victor, A. H. Montgomery, & M. N. Lubell (Eds.), *The Oxford Handbook of political networks* (pp. 301–325). Oxford University Press. @@ -1416,7 +1425,7 @@ Oxford University Press.
-152\. Leifeld, P. (2020). Policy debates and discourse network analysis: +153\. Leifeld, P. (2020). Policy debates and discourse network analysis: A research agenda. *Politics and Governance*, *8*(2), 180–183. @@ -1424,7 +1433,7 @@ A research agenda. *Politics and Governance*, *8*(2), 180–183.
-153\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition +154\. Leifeld, P., & Brandenberger, L. (2019). *Endogenous coalition formation in policy debates*. arXiv Preprint. @@ -1432,7 +1441,7 @@ formation in policy debates*. arXiv Preprint.
-154\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and +155\. Leifeld, P., & Haunss, S. (2012). Political discourse networks and the conflict over software patents in Europe. *European Journal of Political Research*, *51*(3), 382–409. @@ -1441,7 +1450,7 @@ Political Research*, *51*(3), 382–409.
-155\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. +156\. Leifeld, P., Henrichsen, T., Buckton, C., Fergie, G., & Hilton, S. (2021). Belief system alignment and cross-sectoral advocacy efforts in policy debates. *Journal of European Public Policy*, 1–24. @@ -1450,7 +1459,7 @@ policy debates. *Journal of European Public Policy*, 1–24.
-156\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss +157\. Leipold, A. C. (2016). *Ökonomische Ungleichheit und der Einfluss von Diskurskoalitionen auf Vermögensbesteuerung in Deutschland, 1995–2015: Eine Diskursnetzwerkanalyse von Policy-Wandel in der Steuerpolitik* \[Master's Thesis, FernUniversitĂ€t Hagen, FakultĂ€t fĂŒr @@ -1461,7 +1470,7 @@ Kultur- und Sozialwissenschaften, Institut fĂŒr Politikwissenschaft\].
-157\. Leipold, A. (2024). *Der Einfluss von Diskurskoalitionen auf die +158\. Leipold, A. (2024). *Der Einfluss von Diskurskoalitionen auf die Besteuerung von Unternehmen. Ein Vergleich von Pressedebatten und Anhörungen im Deutschen Bundestag zur Steuergesetzgebung seit 1965* \[Doctoral Dissertation, Leuphana UniversitĂ€t LĂŒneburg, FakultĂ€t @@ -1471,7 +1480,7 @@ Staatswissenschaften\].
-158\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in +159\. Lemke, N., Trein, P., & Varone, F. (2023). Agenda-setting in nascent policy subsystems: Issue and instrument priorities across venues. *Policy Sciences*, *56*, 633–655. @@ -1480,7 +1489,7 @@ venues. *Policy Sciences*, *56*, 633–655.
-159\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., +160\. Lestrelin, G., Augusseau, X., David, D., Bourgoin, J., Lagabrielle, E., Seen, D. L., & Degenne, P. (2017). Collaborative landscape research in Reunion Island: Using spatial modelling and simulation to support territorial foresight and urban planning. *Applied @@ -1490,7 +1499,7 @@ Geography*, *78*, 66–77.
-160\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s +161\. Li, Z., Tan, X., & Liu, B. (2023). Policy changes in china’s family planning: Perspectives of advocacy coalitions. *International Journal of Environmental Research and Public Health*, *20*(6), 5204. @@ -1499,7 +1508,7 @@ Journal of Environmental Research and Public Health*, *20*(6), 5204.
-161\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, +162\. Lintner, T., DiviĂĄk, T., NekardovĂĄ, B., Lehotskỳ, L., & VaĆĄečka, M. (2023). Slovak MPs’ response to the 2022 Russian invasion of Ukraine in light of conspiracy theories and the polarization of political discourse. *Humanities and Social Sciences Communications*, *10*(1), @@ -1509,7 +1518,7 @@ discourse. *Humanities and Social Sciences Communications*, *10*(1),
-162\. Ličková, V. (2023). *Coal framing in the Indian political +163\. Ličková, V. (2023). *Coal framing in the Indian political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1517,7 +1526,7 @@ Studies\].
-163\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in +164\. Lindberg, M. B., & Kammermann, L. (2021). Advocacy coalitions in the acceleration phase of the European energy transition. *Environmental Innovation and Societal Transitions*, *40*, 262–282. @@ -1526,7 +1535,7 @@ Innovation and Societal Transitions*, *40*, 262–282.
-164\. Lockhart, C. (2014). *Discourse network analysis of the Northern +165\. Lockhart, C. (2014). *Discourse network analysis of the Northern Gateway Pipeline project: Assessing environmental governance in the joint review panel process* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental @@ -1536,7 +1545,7 @@ Governance Section\].
-165\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & +166\. MalkamÀki, A., YlÀ-Anttila, T., Brockhaus, M., Toppinen, A., & Wagner, P. M. (2021). Unity in diversity? When advocacy coalitions and policy beliefs grow trees in South Africa. *Land Use Policy*, *102*, 105283. @@ -1545,7 +1554,7 @@ policy beliefs grow trees in South Africa. *Land Use Policy*, *102*,
-166\. MalkamÀki, A. (2019). *On the human impacts and governance of +167\. MalkamÀki, A. (2019). *On the human impacts and governance of large-scale tree plantations* \[Doctoral Dissertation, University of Helsinki, Faculty of Agriculture; Forestry\]. @@ -1554,7 +1563,7 @@ Helsinki, Faculty of Agriculture; Forestry\].
-167\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & +168\. MalkamÀki, A., Chen, T. H. Y., Gronow, A., KivelÀ, M., Vesa, J., & YlÀ-Anttila, T. (2023). *Complex coalitions: Political alliances across relational contexts*. arXiv:2308.14422. @@ -1563,7 +1572,7 @@ relational contexts*. arXiv:2308.14422.
-168\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & +169\. MalkamĂ€ki, A., Wagner, P. M., Brockhaus, M., Toppinen, A., & YlĂ€-Anttila, T. (2021). On the acoustics of policy learning: Can co-participation in policy forums break up echo chambers? *Policy Studies Journal*, *49*(2), 431–456. @@ -1572,7 +1581,7 @@ Studies Journal*, *49*(2), 431–456.
-169\. Mardiah, A. N. R. (2018). *Interface between disaster and +170\. Mardiah, A. N. R. (2018). *Interface between disaster and development: Local economic revival through collaborative post-disaster recovery governance and network in Indonesia* \[PhD thesis, University of Leeds, School of Geography\]. @@ -1582,7 +1591,7 @@ of Leeds, School of Geography\].
-170\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward +171\. Mardiah, A. N., Lovett, J. C., & Evanty, N. (2017). Toward integrated and inclusive disaster risk reduction in Indonesia: Review of regulatory frameworks and institutional networks. In R. Djalante, M. Garschagen, F. Thomalla, & R. Shaw (Eds.), *Disaster risk reduction in @@ -1593,7 +1602,7 @@ Indonesia* (pp. 57–84). Springer.
-171\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. +172\. Marino, F., Crowley, S. L., Williams Foley, N. A., McDonald, R. A., & Hodgson, D. J. (2023). Stakeholder discourse coalitions and polarisation in the hen harrier conservation debate in news media. *People and Nature*, *5*(2), 668–683. @@ -1603,7 +1612,7 @@ polarisation in the hen harrier conservation debate in news media.
-172\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the +173\. Mariño, D., & Rozenblat, C. (2022). Stakeholders’ power in the networking structuration processes of the urban resilience concept in Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1), 46–57. @@ -1612,7 +1621,7 @@ Habitat III agenda (2012–2016). *Geography and Sustainability*, *3*(1),
-173\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing +174\. Markard, J., Rinscheid, A., & Widdel, L. (2021). Analyzing transitions through the lens of discourse networks: Coal phase-out in Germany. *Environmental Innovation and Societal Transitions*, *40*, 315–331. @@ -1621,7 +1630,7 @@ Germany. *Environmental Innovation and Societal Transitions*, *40*,
-174\. Mastroianni, L. (2024). How do crises affect policy subsystems? +175\. Mastroianni, L. (2024). How do crises affect policy subsystems? The evolution of policy core beliefs in the EU asylum policy. *JCMS: Journal of Common Market Studies*. @@ -1629,7 +1638,7 @@ Journal of Common Market Studies*.
-175\. Mayer, C. D. (2022). *New west tension and threatened species +176\. Mayer, C. D. (2022). *New west tension and threatened species protection: The western Joshua tree conservation debate in the Morongo Basin, California* \[Master's Thesis, California State University, Long Beach, Department of Geography\]. @@ -1639,7 +1648,7 @@ Beach, Department of Geography\].
-176\. McDonald, E. (2019). *Energy security in the age of +177\. McDonald, E. (2019). *Energy security in the age of interconnection: Cyber-threat framing in British political discourse* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -1648,7 +1657,7 @@ interconnection: Cyber-threat framing in British political discourse*
-177\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of +178\. Mijailoff, J. D., & Burns, S. L. (2023). Fixing the meaning of floating signifier: Discourses and network analysis in the bioeconomy policy processes in Argentina and Uruguay. *Forest Policy and Economics*, *154*, 103039. @@ -1658,7 +1667,7 @@ Economics*, *154*, 103039.
-178\. Miles, A. (2020). *Changes in social networks and narratives +179\. Miles, A. (2020). *Changes in social networks and narratives associated with Lake Erie water quality management after the 2014 Toledo water crisis* \[Master's Thesis, The Ohio State University, Graduate Program in Environment; Natural Resources\]. @@ -1668,7 +1677,7 @@ Program in Environment; Natural Resources\].
-179\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear +180\. Minin, N. (2020). *Post-Fukushima discourse regarding nuclear energy in the European Union and its implications* \[Doctoral Dissertation, Masaryk University, Department of International Relations; European Studies\]. @@ -1677,7 +1686,7 @@ European Studies\].
-180\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes +181\. Miörner, J., Heiberg, J., & Binz, C. (2022). How global regimes diffuse in space – explaining a missed transition in San Diego’s water sector. *Environmental Innovation and Societal Transitions*, *44*, 29–47. @@ -1686,7 +1695,7 @@ sector. *Environmental Innovation and Societal Transitions*, *44*,
-181\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, +182\. Miơić, M., & Obydenkova, A. (2021). Environmental conflict, renewable energy, or both? Public opinion on small hydropower plants in Serbia. *Post-Communist Economies*, *34*(5), 684–713. @@ -1695,7 +1704,7 @@ Serbia. *Post-Communist Economies*, *34*(5), 684–713.
-182\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). +183\. Möck, M., Vogeler, C. S., Bandelow, N. C., & Hornung, J. (2023). Relational coupling of multiple streams: The case of COVID-19 infections in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374. @@ -1704,7 +1713,7 @@ in German abattoirs. *Policy Studies Journal*, *51*(2), 351–374.
-183\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? +184\. Morton, S. E., Muchiri, J., & Swiss, L. (2020). Which feminism(s)? For whom? Intersectionality in Canada’s feminist international assistance policy. *International Journal*, *75*(3), 329–348. @@ -1713,7 +1722,7 @@ assistance policy. *International Journal*, *75*(3), 329–348.
-184\. Muller, A. (2014). Het meten van discourscoalities met +185\. Muller, A. (2014). Het meten van discourscoalities met discoursnetwerkanalyse: Naar een formele analyse van het politieke vertoog. *Res Publica*, *56*(3), 337–364. @@ -1722,7 +1731,7 @@ vertoog. *Res Publica*, *56*(3), 337–364.
-185\. Muller, A. (2015). Using discourse network analysis to measure +186\. Muller, A. (2015). Using discourse network analysis to measure discourse coalitions: Towards a formal analysis of political discourse. *World Political Science*, *11*(2), 377–404. @@ -1731,7 +1740,7 @@ discourse coalitions: Towards a formal analysis of political discourse.
-186\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big +187\. Murti, D. C. W., & Nur Ratriyana, I. (2022). The playground of big tobacco? Discourse network analysis of the cigarette advertising debate and policy in Indonesia. *Journal of Communication Inquiry*. @@ -1740,7 +1749,7 @@ and policy in Indonesia. *Journal of Communication Inquiry*.
-187\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine +188\. Nagel, M. (2015). *Polarisierung im politischen Diskurs: Eine Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS. @@ -1748,7 +1757,7 @@ Netzwerkanalyse zum Konflikt um “Stuttgart 21”*. Springer VS.
-188\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den +189\. Nagel, M. (2023). Allianzen und Strategien fĂŒr saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 153–177). transcript Verlag. @@ -1758,7 +1767,7 @@ politische Konzepte* (pp. 153–177). transcript Verlag.
-189\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere +190\. Nagel, M. (2023). Divergierende Interessen: Der Kampf um saubere Luft in den StĂ€dten. In J. Betz, H.-J. Bieling, A. Futterer, M. Möhring-Hesse, & M. Nagel (Eds.), *Konflikte um Infrastrukturen: Öffentliche Debatten und politische Konzepte* (pp. 77–98). transcript @@ -1768,7 +1777,7 @@ Verlag.
-190\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level +191\. Nagel, M., & Bravo-Laguna, C. (2022). Analyzing multi-level governance dynamics from a discourse network perspective: The debate over air pollution regulation in Germany. *Environmental Sciences Europe*, *34*(62), 1–18. @@ -1777,7 +1786,7 @@ Europe*, *34*(62), 1–18.
-191\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A +192\. Nagel, M., & Satoh, K. (2019). Protesting iconic megaprojects. A discourse network analysis of the evolution of the conflict over Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700. @@ -1786,7 +1795,7 @@ Stuttgart 21. *Urban Studies*, *56*(8), 1681–1700.
-192\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate +193\. Nagel, M., & SchĂ€fer, M. (2023). Powerful stories of local climate action: Comparing the evolution of narratives using the “narrative rate” index. *Review of Policy Research*, *40*(6), 1093–1119. @@ -1795,7 +1804,7 @@ index. *Review of Policy Research*, *40*(6), 1093–1119.
-193\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die +194\. NÀgler, R. (2019). *Steuermannskunst im Hochschulmanagement. Die Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS. @@ -1803,7 +1812,7 @@ Wirkungskraft von Ideen und Diskursen auf die UniversitÀt*. Springer VS.
-194\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in +195\. Nam, A., & Weible, C. M. (2023). Examining experts’ discourse in South Korea’s nuclear power policy making: An advocacy coalition framework approach to policy knowledge. *Politics & Policy*, *51*(2), 201–221. @@ -1812,7 +1821,7 @@ framework approach to policy knowledge. *Politics & Policy*, *51*(2),
-195\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames +196\. Nam, A., Weible, C. M., & Park, K. (2022). Polarization and frames of advocacy coalitions in South Korea’s nuclear energy policy. *Review of Policy Research*, *39*(4), 387–410. @@ -1821,7 +1830,7 @@ of Policy Research*, *39*(4), 387–410.
-196\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position +197\. Nebel, K. (2016). Religion im moralpolitischen diskurs. Position und Einfluss der Kirchen in der deutschen Debatte um die embryonale Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.), *Religionspolitik und Politik der Religionen in Deutschland* (pp. @@ -1831,7 +1840,7 @@ Stammzellenforschung. In A. Liedhegener & G. Pickel (Eds.),
-197\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting +198\. Nolte, I. M., Polzer, T., & Seiwald, J. (2021). Gender budgeting in emerging economies – a systematic literature review and research agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820. @@ -1840,7 +1849,7 @@ agenda. *Journal of Accounting in Emerging Economies*, *11*(5), 799–820.
-198\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ +199\. OcelĂ­k, P. (2015). *Analỳza diskursivnĂ­ch sĂ­tĂ­: Pƙípad lokĂĄlnĂ­ opozice vƯči hlubinnĂ©mu ĂșloĆŸiĆĄti radioaktivnĂ­ch odpadĆŻ v ČeskĂ© republice* \[PhD thesis, Masarykova univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\]. @@ -1849,7 +1858,7 @@ univerzita, Fakulta sociĂĄlnĂ­ch studiĂ­\].
-199\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech +200\. Ocelı́k, P. (2022). Climate change scepticism in front-page Czech newspaper coverage: A one man show. In D. Tindall, M. C. J. Stoddart, & R. E. Dunlap (Eds.), *Handbook of anti-environmentalism* (pp. 84–106). Edward Elgar Publishing. @@ -1858,7 +1867,7 @@ Edward Elgar Publishing.
-200\. Ohlendorf, N. (2022). *The political economy of energy +201\. Ohlendorf, N. (2022). *The political economy of energy transitions* \[Doctoral Thesis, Technical University of Berlin, FakultĂ€t VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\]. @@ -1867,7 +1876,7 @@ VI - Planen Bauen Umwelt, FG Ökonomie des Klimawandels\].
-201\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in +202\. Ohlendorf, N., Löhr, M., & Markard, J. (2023). Actors in multi-sector transitions – discourse analysis on hydrogen in Germany. *Environmental Innovation and Societal Transitions*, *47*, 100692. @@ -1876,7 +1885,7 @@ multi-sector transitions – discourse analysis on hydrogen in Germany.
-202\. Ohno, T. (2022). Advocacy coalition framework in environmental +203\. Ohno, T. (2022). Advocacy coalition framework in environmental governance studies: Explaining major policy change for a large dam removal in Japan. *International Review of Public Policy*, *4*(1). @@ -1885,7 +1894,7 @@ removal in Japan. *International Review of Public Policy*, *4*(1).
-203\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa +204\. Oliveira, H. C., & Sanches Filho, A. (2023). CoalizĂ”es de defesa na polĂ­tica de transporte marĂ­timo de cargas no Brasil: Privatização, descentralização e abertura para o capital estrangeiro. *Caderno CRH – Revista de CiĂȘncias Sociais Do Centro de Estudos Pesquisas e Humanidades @@ -1896,7 +1905,7 @@ Da Universidade Federal Da Bahia*, *36*(2), 1–21.
-204\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of +205\. Osei-Kojo, A. (2021). *An advocacy coalition framework analysis of oil and gas governance in Ghana* \[PhD thesis, University of Colorado Denver, School of Public Affairs\]. @@ -1905,7 +1914,7 @@ Denver, School of Public Affairs\].
-205\. Osei-Kojo, A. (2023). Analysing the stability of advocacy +206\. Osei-Kojo, A. (2023). Analysing the stability of advocacy coalitions and policy frames in Ghana’s oil and gas governance. *Policy & Politics*, *51*(1), 71–90. @@ -1914,7 +1923,7 @@ coalitions and policy frames in Ghana’s oil and gas governance. *Policy
-206\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, +207\. Osička, J., Lehotskỳ, L., ZapletalovĂĄ, V., Černoch, F., & Dančák, B. (2018). Natural gas market integration in the Visegrad 4 region: An example to follow or to avoid? *Energy Policy*, *112*, 184–197. @@ -1923,7 +1932,7 @@ example to follow or to avoid? *Energy Policy*, *112*, 184–197.
-207\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & +208\. Padó, S., Blessing, A., Blokker, N., Dayanık, E., Haunss, S., & Kuhn, J. (2019). Who sides with whom? Towards computational construction of discourse networks for political debates. *Proceedings of the 57th Annual Meeting of the Association for Computational Linguistics*, @@ -1933,7 +1942,7 @@ Annual Meeting of the Association for Computational Linguistics*,
-208\. Palladino, N. (2021). The role of epistemic communities in the +209\. Palladino, N. (2021). The role of epistemic communities in the “constitutionalization” of internet governance: The example of the European Commission high-level expert group on artificial intelligence. *Telecommunications Policy*, *45*(6), 102149. @@ -1943,7 +1952,7 @@ European Commission high-level expert group on artificial intelligence.
-209\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, +210\. Pham-Truffert, M., Metz, F., Fischer, M., Rueff, H., & Messerli, P. (2020). Interactions among sustainable development goals: Knowledge for identifying multipliers and virtuous cycles. *Sustainable Development*, *28*(5), 1236–1250. @@ -1952,7 +1961,7 @@ Development*, *28*(5), 1236–1250.
-210\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des +211\. Pic, P. (2022). *Une sĂ©curitĂ© arctique? Analyse des Ă©chelles de la sĂ©curitĂ© dans une rĂ©gion Ă  gĂ©omĂ©trie variable* \[PhD thesis, UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\]. @@ -1962,7 +1971,7 @@ UniversitĂ© Laval, Qu’ebec, Graduate School of International Studies\].
-211\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and +212\. Piereder, J., Janzwood, S., & Homer-Dixon, T. (2023). Ideology and climate change. A complex reflexive systems approach to energy transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.), *The routledge handbook of ideology and international relations* (pp. @@ -1972,7 +1981,7 @@ transition discourse networks. In J. Leader Maynard & M. L. Haas (Eds.),
-212\. PlĂŒmer, S. (2024). *Kongruentes Policy-Lernen als lernbedingter +213\. PlĂŒmer, S. (2024). *Kongruentes Policy-Lernen als lernbedingter Policy-Wandel. Zum Koordinationsmechanismus des Policy-Lernens in Regierungsformationen*. Springer VS. @@ -1981,7 +1990,7 @@ Regierungsformationen*. Springer VS.
-213\. A., P. I., Alekseev, D. V., & Zaripov, N. A. (2024). “Not by deed, +214\. A., P. I., Alekseev, D. V., & Zaripov, N. A. (2024). “Not by deed, but by word”: A discourse network analysis of parliamentary discussions of the bill on raising the retirement age in Russia. *Monitoring of Public Opinion: Economic and Social Changes*, *2*, 116–140. @@ -1991,7 +2000,7 @@ Public Opinion: Economic and Social Changes*, *2*, 116–140.
-214\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing +215\. Pop, I., Gielens, E., & Kottmann, H. (2023). Microdosing psychedelics: The segregation of spiritual and scientific narratives within the r/microdosing online community. *Journal of Psychedelic Studies*, *7*(2), 119–128. @@ -2000,7 +2009,7 @@ Studies*, *7*(2), 119–128.
-215\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse +216\. PospÄ±ÌĆĄilovĂĄ, T. (2022). *Liberty, equality, hydrogen? Discourse network analysis of French hydrogen politics* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2008,7 +2017,7 @@ University, Faculty of Social Studies\].
-216\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking +217\. Pratama, B. I., & Illahi Ulfa, A. A. (2017). Discourse networking analysis as alternative research method in communication science studies – discourse networking analysis sebagai metode penelitian alternatif dalam kajian ilmu komunikasi. *Jurnal Penelitian Komunikasi Dan Opini @@ -2018,7 +2027,7 @@ Publik*, *21*(2), 126–136.
-217\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). +218\. Pratiwi, M., Murtiningsih, B. S. E., & Juliadi, R. (2023). Discourse network analysis pada stakeholder dan integrated value creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*, *7*(2), 256–274. @@ -2027,7 +2036,7 @@ creation dalam CSR Bank Mandiri. *Jurnal Komunikasi Profesional*,
-218\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., +219\. Putri, R. A., Alemmario, R., Melinda, G., Audwina, A. H., Espressivo, A., Herlinda, O., Meilissa, Y., & Saminarsih, D. S. (2023). The advocacy coalition of sugar-sweetened beverage taxes in Indonesia. *BMJ Global Health*, *8*(Suppl 8), 1–13. @@ -2037,7 +2046,7 @@ The advocacy coalition of sugar-sweetened beverage taxes in Indonesia.
-219\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder +220\. Rantala, S., & Di Gregorio, M. (2014). Multistakeholder environmental governance in action: REDD+ discourse coalitions in Tanzania. *Ecology and Society*, *19*(2), 66–76. @@ -2046,7 +2055,7 @@ Tanzania. *Ecology and Society*, *19*(2), 66–76.
-220\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education +221\. Reckhow, S., & Tompkins-Stange, M. (2018). Financing the education policy discourse: Philanthropic funders as entrepreneurs in policy networks. *Interest Groups & Advocacy*, *7*(3), 258–288. @@ -2055,7 +2064,7 @@ networks. *Interest Groups & Advocacy*, *7*(3), 258–288.
-221\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the +222\. Reckhow, S., Tompkins-Stange, M., & Galey-Horn, S. (2021). How the political economy of knowledge production shapes education policy: The case of teacher evaluation in federal policy discourse. *Educational Evaluation and Policy Analysis*, *43*(3), 472–494. @@ -2065,7 +2074,7 @@ Evaluation and Policy Analysis*, *43*(3), 472–494.
-222\. Rennkamp, B. (2019). Power, coalitions and institutional change in +223\. Rennkamp, B. (2019). Power, coalitions and institutional change in South African climate policy. *Climate Policy*, *19*(6), 756–770. @@ -2073,7 +2082,7 @@ South African climate policy. *Climate Policy*, *19*(6), 756–770.
-223\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. +224\. Rennkamp, B., Haunss, S., Wongsa, K., Ortega, A., & Casamadrid, E. (2017). Competing coalitions: The politics of renewable energy and fossil fuels in Mexico, South Africa and Thailand. *Energy Research & Social Science*, *34*, 214–223. @@ -2083,7 +2092,7 @@ Social Science*, *34*, 214–223.
-224\. Rinscheid, A. (2018). *Behavioral and institutionalist +225\. Rinscheid, A. (2018). *Behavioral and institutionalist perspectives on preference formation in a contested political context: The case of divesting from nuclear power* \[Doctoral Dissertation, University of St. Gallen, School of Management, Economics, Law, Social @@ -2094,7 +2103,7 @@ Sciences; International Affairs\].
-225\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy +226\. Rinscheid, A. (2015). Crisis, policy discourse, and major policy change: Exploring the role of subsystem polarization in nuclear energy policymaking. *European Policy Analysis*, *1*(2), 34–70. @@ -2103,7 +2112,7 @@ policymaking. *European Policy Analysis*, *1*(2), 34–70.
-226\. Rinscheid, A. (2020). Business power in noisy politics: An +227\. Rinscheid, A. (2020). Business power in noisy politics: An exploration based on discourse network analysis and survey data. *Politics and Governance*, *8*(2), 286–297. @@ -2112,7 +2121,7 @@ exploration based on discourse network analysis and survey data.
-227\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. +228\. Rinscheid, A., Eberlein, B., Emmenegger, P., & Schneider, V. (2020). Why do junctures become critical? Political discourse, agency, and joint belief shifts in comparative perspective. *Regulation & Governance*, *14*(4), 653–673. @@ -2121,7 +2130,7 @@ Governance*, *14*(4), 653–673.
-228\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The +229\. Rone, J. (2018). *“Don’t worry, we are from the internet.” The diffusion of protest against the anti-counterfeiting trade agreement in the age of austerity* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2131,7 +2140,7 @@ Department of Political; Social Sciences\].
-229\. Rone, J. (2023). Instrumentalising sovereignty claims in British +230\. Rone, J. (2023). Instrumentalising sovereignty claims in British pro- and anti-Brexit mobilisations. *The British Journal of Politics and International Relations*, *25*(3), 444–461. @@ -2140,7 +2149,7 @@ International Relations*, *25*(3), 444–461.
-230\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff +231\. Rosalia, F. (2023). Discourse battle on Borobudur Temple tariff increase policy in discourse analysis network. *Jurnal Komunikasi*, *17*(1), 62–75. @@ -2148,7 +2157,7 @@ increase policy in discourse analysis network. *Jurnal Komunikasi*,
-231\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, +232\. Rychlik, J., Hornung, J., & Bandelow, N. C. (2021). Come together, right now: Storylines and social identities in coalition building in a local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247. @@ -2157,7 +2166,7 @@ local policy subsystem. *Politics & Policy*, *49*(5), 1216–1247.
-232\. Samatan, N., Nurhasanah, N., Londol, M., Robingah, R., & Fania, S. +233\. Samatan, N., Nurhasanah, N., Londol, M., Robingah, R., & Fania, S. (2024). Critical discourse network analysis on Ferdy Sambo news in online media using agenda setting theory. *Jurnal Komunikasi Ikatan Sarjana Komunikasi Indonesia*, *9*(1), 146–167. @@ -2167,7 +2176,7 @@ Sarjana Komunikasi Indonesia*, *9*(1), 146–167.
-233\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, +234\. Saputro, N. P., Pearly, C., Hidayat, E., Charoline, E., & Wulan, R. R. (2023). Bias media mengenai pemberitaan batalnya Indonesia menjadi tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co. *Jurnal Ilmiah Wahana Pendidikan*, *9*(23), 547–556. @@ -2177,7 +2186,7 @@ tuan rumah piala dunia u-20 2023 di media online tirto.id dan tempo.co.
-234\. Schaub, S. (2021). *The politics of water protection* \[Doctoral +235\. Schaub, S. (2021). *The politics of water protection* \[Doctoral Dissertation, University of Heidelberg, Faculty of Economics; Social Studies, Institute of Political Science\]. @@ -2186,7 +2195,7 @@ Studies, Institute of Political Science\].
-235\. Schaub, S. (2021). Public contestation over agricultural +236\. Schaub, S. (2021). Public contestation over agricultural pollution: A discourse network analysis on narrative strategies in the policy process. *Policy Sciences*, *54*(4), 783–821. @@ -2195,7 +2204,7 @@ policy process. *Policy Sciences*, *54*(4), 783–821.
-236\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable +237\. Schaub, S., & Braunbeck, T. (2020). Transition towards sustainable pharmacy? The influence of public debates on policy responses to pharmaceutical contaminants in water. *Environmental Sciences Europe*, *32*(1), 1–19. @@ -2204,7 +2213,7 @@ pharmaceutical contaminants in water. *Environmental Sciences Europe*,
-237\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy +238\. Schaub, S., & Metz, F. A. (2020). Comparing discourse and policy network approaches: Evidence from water policy on micropollutants. *Politics and Governance*, *8*(2), 184–199. @@ -2213,7 +2222,7 @@ network approaches: Evidence from water policy on micropollutants.
-238\. Schmid, N. (2020). *The politics of technological change – case +239\. Schmid, N. (2020). *The politics of technological change – case studies from the energy sector* \[Doctoral Thesis, ETH ZĂŒrich, Department of Humanities, Social; Political Sciences, Energy; Technology Policy Group\]. @@ -2222,7 +2231,7 @@ Policy Group\].
-239\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining +240\. Schmid, N., Sewerin, S., & Schmidt, T. S. (2020). Explaining advocacy coalition change with policy feedback. *Policy Studies Journal*, *48*(4), 1109–1134. @@ -2230,7 +2239,7 @@ Journal*, *48*(4), 1109–1134.
-240\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A +241\. Schmidt, S. (2017). *Terrorist attacks as “policy windows”: A discourse network analysis of German parliamentary debates* \[Diploma Thesis, Charles University, Department of Security Studies\]. @@ -2239,7 +2248,7 @@ Thesis, Charles University, Department of Security Studies\].
-241\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, +242\. Schmidt, T. S., Schmid, N., & Sewerin, S. (2019). Policy goals, partisanship and paradigmatic change in energy policy – analyzing parliamentary discourse in Germany over 30 years. *Climate Policy*, *19*(6), 771–786. @@ -2248,7 +2257,7 @@ parliamentary discourse in Germany over 30 years. *Climate Policy*,
-242\. Schmitz, L. (2018). *From coherence to coheritization. Explaining +243\. Schmitz, L. (2018). *From coherence to coheritization. Explaining the rise of policy coherence in EU external policy* \[Master's Thesis, Radboud University Nijmegen, Faculteit der Managementwetenschappen\]. @@ -2257,7 +2266,7 @@ Radboud University Nijmegen, Faculteit der Managementwetenschappen\].
-243\. Schmitz, L., & Eimer, T. R. (2020). From coherence to +244\. Schmitz, L., & Eimer, T. R. (2020). From coherence to coheritization: Explaining the rise of policy coherence in EU external policy. *Globalizations*, *17*(4), 629–647. @@ -2266,7 +2275,7 @@ policy. *Globalizations*, *17*(4), 629–647.
-244\. Schmitz, L., & Seidl, T. (2022). As open as possible, as +245\. Schmitz, L., & Seidl, T. (2022). As open as possible, as autonomous as necessary: Understanding the rise of open strategic autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*, *61*(3), 834–852. @@ -2275,7 +2284,7 @@ autonomy in EU trade policy. *JCMS: Journal of Common Market Studies*,
-245\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of +246\. Schneider, N., & Rinscheid, A. (2024). The (de-)construction of technology legitimacy: Contending storylines surrounding wind energy in Austria and Switzerland. *Technological Forecasting and Social Change*, *198*, 122929. @@ -2284,7 +2293,7 @@ Austria and Switzerland. *Technological Forecasting and Social Change*,
-246\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and +247\. Schneider, V., & Ollmann, J. K. (2013). Punctuations and displacements in policy discourse: The climate change issue in Germany 2007–2010. In S. Silvern & S. Young (Eds.), *Environmental change and sustainability* (pp. 157–184). Intech. @@ -2293,7 +2302,7 @@ sustainability* (pp. 157–184). Intech.
-247\. Schulz, C. (2020). Forest conservation through markets? A +248\. Schulz, C. (2020). Forest conservation through markets? A discourse network analysis of the debate on funding mechanisms for REDD+ in Brazil. *Environmental Communication*, *14*(2), 202–218. @@ -2302,7 +2311,7 @@ in Brazil. *Environmental Communication*, *14*(2), 202–218.
-248\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of +249\. Sconfienza, U., & Durand, F. (2023). Discourse network analysis of Twitter and newspapers: Lessons learned from the nuclear debate in the 2022 French presidential campaign. *French Politics*, *21*(2), 195–221. @@ -2311,7 +2320,7 @@ Twitter and newspapers: Lessons learned from the nuclear debate in the
-249\. Sconfienza, U. M., & Durand, F. (2024). Contested nuclear +250\. Sconfienza, U. M., & Durand, F. (2024). Contested nuclear discourses during the 2022 French presidential campaign. *Journal of Environmental Policy & Planning*. @@ -2320,7 +2329,7 @@ Environmental Policy & Planning*.
-250\. Seidl, T. (2021). *Ideas, politics, and technological change: +251\. Seidl, T. (2021). *Ideas, politics, and technological change: Essays on the comparative political economy of digital capitalism* \[PhD thesis, European University Institute, Department of Political; Social Sciences\]. @@ -2329,7 +2338,7 @@ Sciences\].
-251\. Seidl, T. (2022). The politics of platform capitalism: A case +252\. Seidl, T. (2022). The politics of platform capitalism: A case study on the regulation of Uber in New York. *Regulation & Governance*, *16*(2), 357–374. @@ -2337,7 +2346,7 @@ study on the regulation of Uber in New York. *Regulation & Governance*,
-252\. Selle, L. (2017). What multi-level parliamentary system? +253\. Selle, L. (2017). What multi-level parliamentary system? Parliamentary discourses in EU budgetary negotiations (MFF 2014–2020). In S. Becker, M. W. Bauer, & A. De Feo (Eds.), *The new politics of the European Union budget* (pp. 149–172). Nomos. @@ -2347,7 +2356,7 @@ European Union budget* (pp. 149–172). Nomos.
-253\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z +254\. Ć evčík, M. (2021). *Analỳza vyƙazenı́ jadernĂ© energie z energetickĂ©ho mixu Německa po roce 2011* \[Diploma Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2355,7 +2364,7 @@ University, Faculty of Social Studies\].
-254\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris +255\. Shukla, R., & Swarnakar, P. (2022). Energy justice in post-Paris India: Unpacking consensus and conflict through storylines and discourse coalitions. *Energy Research & Social Science*, *91*, 102687. @@ -2364,7 +2373,7 @@ coalitions. *Energy Research & Social Science*, *91*, 102687.
-255\. Shukla, R., & Swarnakar, P. (2022). Energy transition and +256\. Shukla, R., & Swarnakar, P. (2022). Energy transition and dialectics: Tracing discursive resistance to coal through discourse coalition in India. *Globalizations*, *20*(8), 1296–1311. @@ -2373,7 +2382,7 @@ coalition in India. *Globalizations*, *20*(8), 1296–1311.
-256\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi +257\. Siagian, T. H. (2020). Mencari kelompok berisiko tinggi terinfeksi virus corona dengan discourse network analysis. *Jurnal Kebijakan Kesehatan Indonesia: JKKI*, *9*(2), 98–106. @@ -2382,7 +2391,7 @@ Kesehatan Indonesia: JKKI*, *9*(2), 98–106.
-257\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying +258\. Sick, H. (2023). From rhetoric to regulation: Inferring lobbying influence on EU efforts to regulate CO2 emissions of cars using network analysis. *Interest Groups & Advocacy*, *12*, 388–412. @@ -2391,7 +2400,7 @@ analysis. *Interest Groups & Advocacy*, *12*, 388–412.
-258\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan +259\. Silalahi, E. (2023). Analisis jaringan wacana pada pembentukan UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 34–49. @@ -2399,7 +2408,7 @@ UUTPKS di media daring. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2),
-259\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada +260\. Sofura, A. M. (2023). Discourse network analysis: Studi kasus pada kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina. *Kommunikatif*, *12*(1), 37–50. @@ -2407,7 +2416,7 @@ kebijakan kenaikan harga Bahan Bakar Minyak (BBM) pertamina.
-260\. Sohn, C. (2023). The impact of rebordering on cross-border +261\. Sohn, C. (2023). The impact of rebordering on cross-border cooperation actors’ discourses in the Öresund region. A semantic network approach. *Geografiska Annaler: Series B, Human Geography*. @@ -2416,7 +2425,7 @@ approach. *Geografiska Annaler: Series B, Human Geography*.
-261\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu +262\. Soraya, R. (2023). Jaringan wacana isu publik: Studi DNA pada isu ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*, *7*(2), 130–145. @@ -2424,7 +2433,7 @@ ASN terpapar radikalisme. *Jurnal Interaksi: Jurnal Ilmu Komunikasi*,
-262\. Stancioff, C. E. (2016). Locality and landscape change: Cultural +263\. Stancioff, C. E. (2016). Locality and landscape change: Cultural values and social-ecological resiliency in the Kalinago territory. In T. Collins, G. Kindermann, C. Newman, & N. Cronin (Eds.), *Landscape values: Place and praxis. Conference, galway, 29th june–2nd july, 2016* @@ -2435,7 +2444,7 @@ values: Place and praxis. Conference, galway, 29th june–2nd july, 2016*
-263\. Starke, J. (2016). *Generating policy change in situations of +264\. Starke, J. (2016). *Generating policy change in situations of equilibrium: Shifting discourse networks in the case of wild circus animals in Germany* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance @@ -2445,7 +2454,7 @@ Section\].
-264\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. +265\. Starke, J. R., Metze, T. A. P., Candel, J. J. L., Dewulf, A. R. P. J., & Termeer, K. J. A. M. (2023). “Green future” versus “planetary boundaries”? Evolving online discourse coalitions in European bioeconomy conflicts. *Journal of Cleaner Production*, *425*, 139058. @@ -2455,7 +2464,7 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
-265\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +266\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2463,16 +2472,25 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-266\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +267\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\].
+
+ +268\. Stoddart, M. C. J., Koop-Monteiro, Y., & Tindall, D. B. (2024). +Instagram as an arena of climate change communication and mobilization: +A discourse network analysis of COP26. *Environmental Communication*. + + +
+
-267\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +269\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2481,7 +2499,7 @@ co-exist?* Palgrave Macmillan.
-268\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +270\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2491,7 +2509,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-269\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +271\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2501,7 +2519,7 @@ alignments through discourse networks and intersectoral relationships.
-270\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +272\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2510,7 +2528,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-271\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +273\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2519,7 +2537,7 @@ Sociologie*, *53*(3), 316–336.
-272\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +274\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2528,7 +2546,7 @@ and the cultural dynamics of multilevel climate governance.
-273\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +275\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2538,7 +2556,7 @@ climate change: Canadian national news and mediated policy networks.
-274\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +276\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy Research*, *40*(6), 1144–1168. @@ -2547,7 +2565,7 @@ Research*, *40*(6), 1144–1168.
-275\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +277\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2556,7 +2574,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-276\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +278\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2565,7 +2583,7 @@ Economics and Environment*, *83*(4), 62–75.
-277\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +279\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2574,7 +2592,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-278\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +280\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2584,7 +2602,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-279\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +281\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2592,7 +2610,7 @@ thesis, Utrecht University\].
-280\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +282\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2601,7 +2619,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-281\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +283\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2610,7 +2628,7 @@ International Conference on Business and Public Administration (AICoBPA
-282\. Taranger, K. K. (2020). *The institutionalisation of climate +284\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2619,7 +2637,7 @@ Environmental Governance Section\].
-283\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +285\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2628,7 +2646,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-284\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +286\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2638,7 +2656,7 @@ Glasgow, School of Social; Political Sciences\].
-285\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +287\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates on hydraulic fracturing. Comparing coalition politics in North America @@ -2649,7 +2667,7 @@ and Europe* (pp. 177–200). Palgrave Macmillan.
-286\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +288\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2658,7 +2676,7 @@ of Policy Research*, *34*(3), 310–330.
-287\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +289\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2667,7 +2685,7 @@ tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu*
-288\. Tuinenburg, J. (2019). *The effect of discourse networks on the +290\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2676,7 +2694,7 @@ Governance\].
-289\. Umansky Casapa, N. (2022). *Securitization and social media +291\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2685,7 +2703,7 @@ Dublin, School of Politics; International Relations\].
-290\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +292\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2693,7 +2711,7 @@ Social Studies\].
-291\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level +293\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level rise in a polycentric system. Easier said than done*. Cambridge University Press. @@ -2701,7 +2719,7 @@ University Press.
-292\. Vaughan, M. (2020). Talking about tax: The discursive distance +294\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2710,7 +2728,7 @@ Politics*, *17*(2), 114–129.
-293\. Vedres, B. (2022). Multivocality and robust action dynamics in +295\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2718,7 +2736,7 @@ political discourse. *Poetics*, *90*, 101576.
-294\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +296\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2727,7 +2745,7 @@ Environmental Change*, *63*, 102117.
-295\. Vogeler, C. S. (2022). The integration of environmental objectives +297\. Vogeler, C. S. (2022). The integration of environmental objectives in the common agricultural policy – partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2736,7 +2754,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-296\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +298\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2745,7 +2763,7 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-297\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +299\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343. @@ -2754,7 +2772,7 @@ the European Parliament. *European Policy Analysis*, *7*, 324–343.
-298\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of +300\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of the far right: How far-right actors become mainstream in public debates. *Political Communication*, *41*(3), 353–372. @@ -2763,7 +2781,7 @@ the far right: How far-right actors become mainstream in public debates.
-299\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +301\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2774,7 +2792,7 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
-300\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of +302\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of failure on socio-technical system configurations* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -2783,7 +2801,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-301\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +303\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2792,7 +2810,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-302\. Wallaschek, S. (2019). The discursive appeal to solidarity and +304\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2800,7 +2818,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-303\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +305\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2810,7 +2828,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-304\. Wallaschek, S. (2020). Analyzing the European parliamentary +306\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2820,7 +2838,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-305\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +307\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2829,7 +2847,7 @@ European Public Policy*, *27*(7), 1034–1053.
-306\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +308\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2838,7 +2856,7 @@ Economy*, *25*(2), 231–247.
-307\. Wallaschek, S. (2020). The discursive construction of solidarity: +309\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2846,7 +2864,7 @@ Studies*, *68*(1), 74–92.
-308\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +310\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2854,7 +2872,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-309\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +311\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2864,7 +2882,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-310\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +312\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2873,7 +2891,7 @@ the public sphere: A discourse network analysis of German newspapers
-311\. Wang, S. (2018). *Dynamic constructed climate change discourses +313\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2883,7 +2901,7 @@ Politics\].
-312\. Wang, S. (2024). *Communicating climate change in China. A dynamic +314\. Wang, S. (2024). *Communicating climate change in China. A dynamic discourse approach*. Palgrave Macmillan. @@ -2891,7 +2909,7 @@ discourse approach*. Palgrave Macmillan.
-313\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +315\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2900,7 +2918,7 @@ Public Policy*, *44*(3), 354–368.
-314\. Wang, Y. (2021). Examining the actor coalitions and discourse +316\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2909,7 +2927,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-315\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +317\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2918,7 +2936,7 @@ Policy Analysis Archives*, *25*(34), 1–27.
-316\. Wang, Y. (2020). Understanding Congressional coalitions: A +318\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30. @@ -2927,7 +2945,7 @@ Student Succeeds act. *Education Policy Analysis Archives*, *28*(119),
-317\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +319\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & Hekkert, M. P. (2024). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. *Environmental Policy and Governance*, *34*(2), 152–165. @@ -2937,7 +2955,7 @@ interest organizations in fragmented socio-technical systems.
-318\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +320\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2946,7 +2964,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-319\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +321\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2956,7 +2974,7 @@ The influence of global and national institutional dynamics.
-320\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using +322\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using collaborative governance to regulate sober living facilities: Structures and strategies for mitigating the influence of powerful actors in multi-sectoral networks. *Administration & Society*, *56*(4), 473–510. @@ -2966,7 +2984,7 @@ multi-sectoral networks. *Administration & Society*, *56*(4), 473–510.
-321\. Wu, J., & Liu, Y. (2020). Deception detection methods +323\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2975,7 +2993,7 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-322\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +324\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948. @@ -2984,7 +3002,7 @@ Information Technology*, *34*(9), 936–948.
-323\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission +325\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. *Higher Education Policy*, *37*, 209–236. @@ -2993,7 +3011,7 @@ policy evolution from 2003 to 2020 in China – a social network analysis.
-324\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +326\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -3002,7 +3020,7 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-325\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +327\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32. @@ -3011,7 +3029,7 @@ infrastructure. *Politics & Policy*, *50*(1), 8–32.
-326\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring +328\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring emotional discourses: The case of COVID-19 protests in the US media. *Administrative Theory & Praxis*, *46*(1), 35–54. @@ -3020,7 +3038,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-327\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +329\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -3029,7 +3047,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-328\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +330\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -3038,7 +3056,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-329\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +331\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), 107–129. @@ -3047,10 +3065,18 @@ transmission line sitings. *Public Policy and Administration*, *38*(1),
-330\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +332\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\].
+
+ +333\. Zunaedi, Z. Z. H. (2024). Jejaring wacana cuitan Twitter pada +kontroversi konten pemeliharaan satwa liar. *Perspektif*, *13*(3), +756–766. + +
+
From 1f4ddb9021a7dea8113fd571a29314bc02f33a1b Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Thu, 8 Aug 2024 18:20:01 +0200 Subject: [PATCH 19/43] Fixed phase transition bug without qualifier variable --- build/bibliography.md | 2 +- dna/src/main/java/dna/HeadlessDna.java | 1 - dna/src/main/java/export/Exporter.java | 29 ++++++++----- rDNA/rDNA/DESCRIPTION | 4 +- rDNA/rDNA/R/rDNA.R | 56 ++++++++++++++++---------- rDNA/rDNA/man/dna_phaseTransitions.Rd | 48 ++++++++++++++-------- 6 files changed, 89 insertions(+), 51 deletions(-) diff --git a/build/bibliography.md b/build/bibliography.md index a26f1601..36506b92 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-07-22 +date: 2024-08-08 title: "Discourse Network Analysis: Bibliography" --- diff --git a/dna/src/main/java/dna/HeadlessDna.java b/dna/src/main/java/dna/HeadlessDna.java index dfe93bce..7755676d 100644 --- a/dna/src/main/java/dna/HeadlessDna.java +++ b/dna/src/main/java/dna/HeadlessDna.java @@ -594,7 +594,6 @@ public void rTimeWindow(String networkType, String statementType, String variabl Dna.logger.log(le); } } - } /** diff --git a/dna/src/main/java/export/Exporter.java b/dna/src/main/java/export/Exporter.java index c0a0c15a..86ca3b86 100644 --- a/dna/src/main/java/export/Exporter.java +++ b/dna/src/main/java/export/Exporter.java @@ -1770,8 +1770,11 @@ public void computeKernelSmoothedTimeSlices() { // save the labels of the variables and qualifier and put indices in hash maps for fast retrieval String[] var1Values = extractLabels(Exporter.this.filteredStatements, Exporter.this.variable1, Exporter.this.variable1Document); String[] var2Values = extractLabels(Exporter.this.filteredStatements, Exporter.this.variable2, Exporter.this.variable2Document); - String[] qualValues = extractLabels(Exporter.this.filteredStatements, Exporter.this.qualifier, Exporter.this.qualifierDocument); - if (dataTypes.get(Exporter.this.qualifier).equals("integer")) { + String[] qualValues = new String[] { "" }; + if (Exporter.this.qualifier != null) { + qualValues = extractLabels(Exporter.this.filteredStatements, Exporter.this.qualifier, Exporter.this.qualifierDocument); + } + if (Exporter.this.qualifier != null && dataTypes.get(Exporter.this.qualifier).equals("integer")) { int[] qual = Exporter.this.originalStatements.stream().mapToInt(s -> (int) s.get(Exporter.this.qualifier)).distinct().sorted().toArray(); if (qual.length < qualValues.length) { qualValues = IntStream.rangeClosed(qual[0], qual[qual.length - 1]) @@ -1788,8 +1791,10 @@ public void computeKernelSmoothedTimeSlices() { var2Map.put(var2Values[i], i); } HashMap qualMap = new HashMap<>(); - for (int i = 0; i < qualValues.length; i++) { - qualMap.put(qualValues[i], i); + if (Exporter.this.qualifier != null) { + for (int i = 0; i < qualValues.length; i++) { + qualMap.put(qualValues[i], i); + } } // create an array list of empty Matrix results, store all date-time stamps in them, and save indices in a hash map @@ -1865,8 +1870,12 @@ public void computeKernelSmoothedTimeSlices() { ArrayList[][][] X = (ArrayList[][][]) new ArrayList[var1Values.length][var2Values.length][qualValues.length]; for (int i = 0; i < var1Values.length; i++) { for (int j = 0; j < var2Values.length; j++) { - for (int k = 0; k < qualValues.length; k++) { - X[i][j][k] = new ArrayList(); + if (Exporter.this.qualifier == null) { + X[i][j][0] = new ArrayList(); + } else { + for (int k = 0; k < qualValues.length; k++) { + X[i][j][k] = new ArrayList(); + } } } } @@ -1909,7 +1918,7 @@ public void computeKernelSmoothedTimeSlices() { var2Index = var2Map.get(((Entity) s.get(Exporter.this.variable2)).getValue()); } int qualIndex = -1; - if (Exporter.this.qualifierDocument) { + if (Exporter.this.qualifierDocument && Exporter.this.qualifier != null) { if (Exporter.this.qualifier.equals("author")) { qualIndex = qualMap.get(s.getAuthor()); } else if (Exporter.this.qualifier.equals("source")) { @@ -1924,7 +1933,9 @@ public void computeKernelSmoothedTimeSlices() { qualIndex = qualMap.get(s.getTitle()); } } else { - if (dataTypes.get(Exporter.this.qualifier).equals("integer") || dataTypes.get(Exporter.this.qualifier).equals("boolean")) { + if (Exporter.this.qualifier == null) { + qualIndex = 0; + } else if (dataTypes.get(Exporter.this.qualifier).equals("integer") || dataTypes.get(Exporter.this.qualifier).equals("boolean")) { qualIndex = qualMap.get(String.valueOf((int) s.get(Exporter.this.qualifier))); } else { qualIndex = qualMap.get(((Entity) s.get(Exporter.this.qualifier)).getValue()); @@ -1988,7 +1999,7 @@ private Matrix processTimeSlice(Matrix matrixResult, ArrayList[ } for (int k2 = 0; k2 < X[0][0].length; k2++) { double qsim = 1.0; - if (!dataTypes.get(Exporter.this.qualifier).equals("short text") && !Exporter.this.qualifierDocument) { + if (Exporter.this.qualifier != null && !dataTypes.get(Exporter.this.qualifier).equals("short text") && !Exporter.this.qualifierDocument) { qsim = Math.abs(1.0 - ((double) Math.abs(k - k2) / (double) Math.abs(X[0][0].length - 1))); } double qdiff = 1.0 - qsim; diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 994b128a..68f46722 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-07-22 +Date: 2024-08-08 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", @@ -31,7 +31,7 @@ Imports: Suggests: testthat, askpass (>= 1.1), - igraph (>= 0.8.1), + igraph (>= 2.0.3), ggraph (>= 2.1.0), cluster (>= 1.12.0), sna (>= 2.4), diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index ae15acd4..9004efe7 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -1722,7 +1722,7 @@ autoplot.dna_network_onemode <- function(object, # Remove tie weights below threshold if (!is.null(threshold)) { - graph <- igraph::delete.edges(graph, which(!igraph::E(graph)$weight >= threshold)) + graph <- igraph::delete_edges(graph, which(!igraph::E(graph)$weight >= threshold)) } # Add node colors @@ -1796,7 +1796,7 @@ autoplot.dna_network_onemode <- function(object, # Remove isolates if (exclude_isolates) { - graph <- igraph::delete.vertices(graph, igraph::degree(graph) == 0) + graph <- igraph::delete_vertices(graph, igraph::degree(graph) == 0) } # Only plot giant component of network. Useful for some plotting algorithms. @@ -3535,7 +3535,7 @@ dna_multiclust <- function(statementType = "DNA Statement", } y[y < 0] <- 0 class(y) <- "matrix" - g <- igraph::graph.adjacency(y, mode = "undirected", weighted = TRUE) + g <- igraph::graph_from_adjacency_matrix(y, mode = "undirected", weighted = TRUE) if (nrow(combined) > 1) { counter_current <- 1 @@ -4472,23 +4472,37 @@ print.dna_multiclust <- function(x, ...) { #' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") #' #' # compute states and phases for sample dataset -#' results2 <- dna_phaseTransitions(distanceMethod = "spectral", -#' clusterMethods = c("ward", -#' "pam", -#' "concor", -#' "walktrap"), -#' k.min = 2, -#' k.max = 6, -#' networkType = "onemode", -#' variable1 = "organization", -#' variable2 = "concept", -#' timeWindow = "days", -#' windowSize = 15, -#' kernel = "gaussian", -#' indentTime = FALSE, -#' normalizeToOne = FALSE) -#' results2 -#' autoplot(results2) +#' results <- dna_phaseTransitions(distanceMethod = "spectral", +#' clusterMethods = c("ward", +#' "pam", +#' "concor", +#' "walktrap"), +#' k.min = 2, +#' k.max = 6, +#' networkType = "onemode", +#' variable1 = "organization", +#' variable2 = "concept", +#' timeWindow = "days", +#' windowSize = 15, +#' kernel = "gaussian", +#' indentTime = FALSE, +#' normalizeToOne = FALSE) +#' results +#' autoplot(results) +#' +#' # access individual plots +#' plots <- autoplot(results) +#' plots[[1]] # show heatmap +#' plots[[2]] # show cluster silhouettes +#' plots[[3]] # show temporal embedding +#' plots[[4]] # show state dynamics +#' +#' # save plots to combined PDF +#' library("ggplotify") # needed to convert heatmap to ggplot diagram +#' library("patchwork") # needed to merge plots into 4 x 4 diagram +#' p1 <- ggplotify::as.ggplot(plots[[1]]) +#' p <- p1 + plots[[2]] + plots[[3]] + plots[[4]] + plot_layout(ncol = 2) +#' ggsave(filename = "phase_transitions.pdf", p, width = 14, height = 12) #' } #' #' @rdname dna_phaseTransitions @@ -4666,7 +4680,7 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", cl <- function(method, distmat) { tryCatch({ similarity_mat <- 1 - distmat - g <- igraph::graph.adjacency(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance + g <- igraph::graph_from_adjacency_matrix(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance if (method %in% hclustMethods) { if (method == "single") { suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "single")) diff --git a/rDNA/rDNA/man/dna_phaseTransitions.Rd b/rDNA/rDNA/man/dna_phaseTransitions.Rd index ce9b8c23..e9286dc2 100644 --- a/rDNA/rDNA/man/dna_phaseTransitions.Rd +++ b/rDNA/rDNA/man/dna_phaseTransitions.Rd @@ -407,23 +407,37 @@ dna_sample() dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") # compute states and phases for sample dataset -results2 <- dna_phaseTransitions(distanceMethod = "spectral", - clusterMethods = c("ward", - "pam", - "concor", - "walktrap"), - k.min = 2, - k.max = 6, - networkType = "onemode", - variable1 = "organization", - variable2 = "concept", - timeWindow = "days", - windowSize = 15, - kernel = "gaussian", - indentTime = FALSE, - normalizeToOne = FALSE) -results2 -autoplot(results2) +results <- dna_phaseTransitions(distanceMethod = "spectral", + clusterMethods = c("ward", + "pam", + "concor", + "walktrap"), + k.min = 2, + k.max = 6, + networkType = "onemode", + variable1 = "organization", + variable2 = "concept", + timeWindow = "days", + windowSize = 15, + kernel = "gaussian", + indentTime = FALSE, + normalizeToOne = FALSE) +results +autoplot(results) + +# access individual plots +plots <- autoplot(results) +plots[[1]] # show heatmap +plots[[2]] # show cluster silhouettes +plots[[3]] # show temporal embedding +plots[[4]] # show state dynamics + +# save plots to combined PDF +library("ggplotify") # needed to convert heatmap to ggplot diagram +library("patchwork") # needed to merge plots into 4 x 4 diagram +p1 <- ggplotify::as.ggplot(plots[[1]]) +p <- p1 + plots[[2]] + plots[[3]] + plots[[4]] + plot_layout(ncol = 2) +ggsave(filename = "phase_transitions.pdf", p, width = 14, height = 12) } } From effd60270350c9bb7ad8e4aa3b3891861f14eae8 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Sat, 10 Aug 2024 17:33:00 +0200 Subject: [PATCH 20/43] Added dna_tidygraph function to rDNA --- build/bibliography.md | 2 +- rDNA/rDNA/DESCRIPTION | 7 +- rDNA/rDNA/NAMESPACE | 1 + rDNA/rDNA/R/rDNA.R | 197 +++++++++++++++++- .../rDNA/man/as.matrix.dna_network_onemode.Rd | 1 + .../rDNA/man/as.matrix.dna_network_twomode.Rd | 1 + rDNA/rDNA/man/autoplot.dna_network.Rd | 1 + rDNA/rDNA/man/dna_network.Rd | 1 + rDNA/rDNA/man/dna_tidygraph.Rd | 150 +++++++++++++ rDNA/rDNA/man/print.dna_network_onemode.Rd | 1 + rDNA/rDNA/man/print.dna_network_twomode.Rd | 1 + 11 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 rDNA/rDNA/man/dna_tidygraph.Rd diff --git a/build/bibliography.md b/build/bibliography.md index 36506b92..dc3aaec0 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-08-08 +date: 2024-08-10 title: "Discourse Network Analysis: Bibliography" --- diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 68f46722..8320c6a0 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-08-08 +Date: 2024-08-10 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", @@ -23,7 +23,7 @@ Maintainer: Philip Leifeld Description: Qualitative content analysis and discourse network analysis using the Java software Discourse Network Analyzer (DNA). Depends: - R (>= 4.0.0) + R (>= 4.1.0) Imports: rJava (>= 0.9-12), ggplot2 (>= 3.3.6), @@ -38,7 +38,8 @@ Suggests: pbmcapply (>= 1.5.1), MASS (>= 7.3-51.5), factoextra (>= 1.0.7), - heatmaply (>= 1.4.2) + heatmaply (>= 1.4.2), + tidygraph (>= 1.3.1) SystemRequirements: Java (>= 11) License: GPL-2 URL: https://github.com/leifeld/dna diff --git a/rDNA/rDNA/NAMESPACE b/rDNA/rDNA/NAMESPACE index 440cc15c..20b7bafa 100644 --- a/rDNA/rDNA/NAMESPACE +++ b/rDNA/rDNA/NAMESPACE @@ -33,6 +33,7 @@ export(dna_printDetails) export(dna_queryCoders) export(dna_sample) export(dna_saveConnectionProfile) +export(dna_tidygraph) importFrom(ggplot2,.pt) importFrom(ggplot2,aes) importFrom(ggplot2,annotate) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 9004efe7..14b49955 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -1820,7 +1820,7 @@ autoplot.dna_network_onemode <- function(object, igraph::E(graph)$weight <- abs(igraph::E(graph)$weight) # Start network plot - g <- ggraph::ggraph(graph, layout = layout) + + g <- ggraph::ggraph(graph, layout = layout, ...) + suppressWarnings(ggraph::geom_edge_link(ggplot2::aes(edge_width = igraph::E(graph)$weight, edge_colour = igraph::E(graph)$color), alpha = edge_alpha, show.legend = FALSE)) + # add edges @@ -1851,6 +1851,201 @@ autoplot.dna_network_onemode <- function(object, #' @export autoplot.dna_network_twomode <- autoplot.dna_network_onemode +#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object +#' +#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object. +#' +#' Convert a \code{dna_network_onemode} or \code{dna_network_twomode} object +#' into a \code{tbl_graph} object as defined in the tidygraph package. These +#' objects can then be plotted using the ggraph package, which contains many +#' network layouts. +#' +#' \code{tbl_graph} objects are an extension of \code{graph} +#' objects defined in the igraph package. Functions for manipulating or plotting +#' the resulting objects from either the tidygraph or igraph package or both +#' can be used. +#' +#' The resulting objects can also be converted to \code{network} objects as +#' defined in the network package (part of the statnet suite of packages) using +#' the \code{asNetwork} function in the intergraph package. +#' +#' @param network A \code{dna_network_onemode} or \code{dna_network_twomode} +#' object to be converted into a \code{tbl_graph} object. Can also be a matrix +#' with edge weights and row and column names for the node labels. +#' @param attributes A \code{dna_attributes} object created using the +#' \link{dna_getAttributes} function with attributes for the nodes in the +#' network. Can also be a data frame with a \code{values} column that contains +#' the node labels and further columns containing the attributes. The +#' attributes are saved as node attributes in the \code{tbl_graph} object. If +#' \code{NULL}, no attributes are included. +#' @param ... Further arguments. Currently not in use. +#' +#' @examples +#' \dontrun{ +#' # prepare toy data +#' dna_sample() +#' dna_openDatabase("sample.dna", coderPassword = "sample") +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "congruence", +#' excludeValues = list(concept = +#' "There should be legislation to regulate emissions.")) +#' at <- dna_getAttributes(variableId = 2) +#' +#' # convert to tbl_graph object +#' g <- dna_tidygraph(nw, at) +#' +#' # basic visualization +#' ggraph::ggraph(g, layout = "fr") + +#' ggraph::geom_edge_link() + +#' ggraph::geom_node_point() +#' +#' # visualization with more bells and whistles +#' ggraph::ggraph(g, layout = "graphopt") + +#' ggraph::geom_edge_link(ggplot2::aes(color = weight, width = weight)) + +#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::scale_edge_color_gradient(low = "azure2", high = "azure4") + +#' ggraph::theme_graph(background = "white") + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) +#' # for more layouts, see vignette("Layouts", package = "ggraph") +#' +#' # hive plot example +#' g <- g |> +#' tidygraph::activate(nodes) |> +#' tidygraph::mutate(centrality = tidygraph::centrality_betweenness()) +#' ggraph::ggraph(g, layout = "hive", axis = Type, sort.by = centrality) + +#' ggraph::geom_edge_hive(ggplot2::aes(colour = "gray", width = weight)) + +#' ggraph::geom_axis_hive(ggplot2::aes(colour = color), +#' size = 5, +#' label = TRUE) + +#' ggraph::scale_edge_color_identity() + +#' theme(legend.position = "none") +#' +#' # example with negative edge weights +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "subtract", +#' excludeValues = list(concept = +#' "There should be legislation to regulate emissions.")) +#' g <- dna_tidygraph(nw, at) +#' ggraph::ggraph(g, layout = "linear", circular = TRUE) + +#' ggraph::geom_edge_arc(aes(color = color, width = abs)) + +#' ggraph::scale_edge_color_identity() + +#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::theme_graph(background = "white") + +#' theme(legend.position = "none") + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) +#' +#' # example with a two-mode network +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "combine") +#' at1 <- dna_getAttributes(statementTypeId = 1, variable = "organization") +#' at2 <- dna_getAttributes(statementTypeId = 1, variable = "concept") +#' at1$Notes <- "organization" +#' at2$Notes <- "concept" +#' at <- rbind(at1, at2) +#' g <- dna_tidygraph(nw, at) +#' ggraph::ggraph(g, layout = "graphopt") + +#' ggraph::geom_edge_link(ggplot2::aes(color = color), width = 1) + +#' ggraph::scale_edge_color_identity() + +#' ggraph::geom_node_point(ggplot2::aes(color = color, shape = Notes), +#' size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) + +#' ggraph::theme_graph(background = "white") + +#' theme(legend.position = "none") +#' } +#' +#' # manipulate and plot using the igraph package +#' library("igraph") +#' class(g) # resulting objects are both tbl_graph and igraph objects +#' igraph::V(g) # get the nodes using igraph functions +#' igraph::E(g) # get the edges using igraph functions +#' igraph::plot(g) # plot network using igraph package +#' +#' # convert to network object (network package, statnet suite of packages) +#' library("intergraph") +#' intergraph::asNetwork(g) +#' +#' @author Philip Leifeld +#' @family {rDNA networks} +#' @export +dna_tidygraph <- function(network, attributes = NULL, ...) { + if (length(intersect(c("dna_network_onemode", "dna_network_twomode", "matrix"), class(network))) < 1) { + stop("The 'network' argument must provide an object created by the 'dna_network' function or a matrix.") + } + if (!is.null(attributes) && (length(intersect(c("dna_attributes", "data.frame"), class(attributes))) < 1) || !"value" %in% colnames(attributes)) { + stop("The 'attributes' argument must be NULL or created by the 'dna_getAttributes' function or a data frame with a 'values' column.") + } + if (!requireNamespace("tidygraph", quietly = TRUE) || packageVersion("tidygraph") < "1.3.1") { + stop("The 'dna_tidygraph' function requires the 'tidygraph' package (>= 1.3.1) to be installed.\n", + "To do this, enter 'install.packages(\"tidygraph\")'.") + } + + if ("dna_network_twomode" %in% class(network)) { + nodes <- data.frame(name = c(rownames(network), colnames(network)), type = c(rep(TRUE, nrow(network)), rep(FALSE, ncol(network))), stringsAsFactors = FALSE) + edges <- data.frame(from = rep(rownames(network), times = ncol(network)), to = rep(colnames(network), each = nrow(network)), weight = as.vector(network)) + edges <- edges[edges$weight != 0, ] + edges$from <- match(edges$from, nodes$name) + edges$to <- match(edges$to, nodes$name) + g <- tidygraph::tbl_graph(nodes = nodes, edges = edges, directed = FALSE) # create tbl_graph object for ggraph + } else if ("dna_network_onemode" %in% class(network)) { + g <- tidygraph::as_tbl_graph(network, directed = FALSE) # create tbl_graph object for ggraph + } else { + stop("Argument supplied by argument 'network' not recognized.") + } + + if (!is.null(attributes)) { + nodes <- tidygraph::as_tibble(g, active = "nodes")$name # extract nodes from graph for matching + at <- attributes[attributes$value %in% nodes, ] # retain only those attributes present in the network + at <- at[match(nodes, at$value), ] # sort attributes in the same order as the nodes in the graph + g <- tidygraph::mutate(g, at[, colnames(at) != "value"]) # embed node attributes in graph + } + + edges <- tidygraph::as_tibble(g, active = "edges") # extract edges from graph + u <- unique(edges$weight) # unique edge weights + combined <- length(u) < 5 && any(grepl("combine", attributes(network)$call)) # combined qualifier aggregation? + edgecol <- sapply(edges$weight, function(weight) { # create edge colors + if (length(u) == 2 & all(sort(u) %in% 0:1) & weight > 0) { # binary: 1 = gray + "gray" + } else if (combined) { # "combined" qualifier aggregation + if (weight == 1) { + "green" + } else if (weight == 2) { + "red" + } else if (weight == 3) { + "blue" + } else { + "gray" + } + } else if (any(u < 0)) { # "subtract" (or something else that generates negative ties) + if (weight < 0) { + "red" + } else { + "green" + } + } else { # any other scale, for example "congruence" qualifier aggregation + "gray" + } + }) + g <- g |> # assign absolute values, edge colors, and sign as edge attributes + tidygraph::activate(edges) |> + tidygraph::mutate(abs = abs(weight), + color = edgecol, + sign = ifelse(weight < 0, "negative", "positive")) + + return(g) +} + # Barplots --------------------------------------------------------------------- diff --git a/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd b/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd index ece2ca6e..f447298d 100644 --- a/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd +++ b/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd @@ -24,6 +24,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_onemode}()}, \code{\link{print.dna_network_twomode}()} } diff --git a/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd b/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd index 4776dd1e..1c15deb4 100644 --- a/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd +++ b/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd @@ -24,6 +24,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_onemode}()}, \code{\link{print.dna_network_twomode}()} } diff --git a/rDNA/rDNA/man/autoplot.dna_network.Rd b/rDNA/rDNA/man/autoplot.dna_network.Rd index 53751857..408bc862 100644 --- a/rDNA/rDNA/man/autoplot.dna_network.Rd +++ b/rDNA/rDNA/man/autoplot.dna_network.Rd @@ -308,6 +308,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{dna_network}()}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_onemode}()}, \code{\link{print.dna_network_twomode}()} } diff --git a/rDNA/rDNA/man/dna_network.Rd b/rDNA/rDNA/man/dna_network.Rd index f6dbba3c..81f3c20b 100644 --- a/rDNA/rDNA/man/dna_network.Rd +++ b/rDNA/rDNA/man/dna_network.Rd @@ -291,6 +291,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_onemode}()}, \code{\link{print.dna_network_twomode}()} } diff --git a/rDNA/rDNA/man/dna_tidygraph.Rd b/rDNA/rDNA/man/dna_tidygraph.Rd new file mode 100644 index 00000000..a64238b5 --- /dev/null +++ b/rDNA/rDNA/man/dna_tidygraph.Rd @@ -0,0 +1,150 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/rDNA.R +\name{dna_tidygraph} +\alias{dna_tidygraph} +\title{Convert a DNA network into a \code{tbl_graph} or \code{graph} object} +\usage{ +dna_tidygraph(network, attributes = NULL, ...) +} +\arguments{ +\item{network}{A \code{dna_network_onemode} or \code{dna_network_twomode} +object to be converted into a \code{tbl_graph} object. Can also be a matrix +with edge weights and row and column names for the node labels.} + +\item{attributes}{A \code{dna_attributes} object created using the +\link{dna_getAttributes} function with attributes for the nodes in the +network. Can also be a data frame with a \code{values} column that contains +the node labels and further columns containing the attributes. The +attributes are saved as node attributes in the \code{tbl_graph} object. If +\code{NULL}, no attributes are included.} + +\item{...}{Further arguments. Currently not in use.} +} +\description{ +Convert a DNA network into a \code{tbl_graph} or \code{graph} object. +} +\details{ +Convert a \code{dna_network_onemode} or \code{dna_network_twomode} object +into a \code{tbl_graph} object as defined in the tidygraph package. These +objects can then be plotted using the ggraph package, which contains many +network layouts. + +\code{tbl_graph} objects are an extension of \code{graph} +objects defined in the igraph package. Functions for manipulating or plotting +the resulting objects from either the tidygraph or igraph package or both +can be used. + +The resulting objects can also be converted to \code{network} objects as +defined in the network package (part of the statnet suite of packages) using +the \code{asNetwork} function in the intergraph package. +} +\examples{ +\dontrun{ +# prepare toy data +dna_sample() +dna_openDatabase("sample.dna", coderPassword = "sample") +nw <- dna_network(networkType = "onemode", + qualifierAggregation = "congruence", + excludeValues = list(concept = + "There should be legislation to regulate emissions.")) +at <- dna_getAttributes(variableId = 2) + +# convert to tbl_graph object +g <- dna_tidygraph(nw, at) + +# basic visualization +ggraph::ggraph(g, layout = "fr") + + ggraph::geom_edge_link() + + ggraph::geom_node_point() + +# visualization with more bells and whistles +ggraph::ggraph(g, layout = "graphopt") + + ggraph::geom_edge_link(ggplot2::aes(color = weight, width = weight)) + + ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + + ggplot2::scale_color_identity() + + ggraph::scale_edge_color_gradient(low = "azure2", high = "azure4") + + ggraph::theme_graph(background = "white") + + ggraph::geom_node_text(ggplot2::aes(label = name), + repel = TRUE, + max.overlaps = 10, + show.legend = FALSE) +# for more layouts, see vignette("Layouts", package = "ggraph") + +# hive plot example +g <- g |> + tidygraph::activate(nodes) |> + tidygraph::mutate(centrality = tidygraph::centrality_betweenness()) +ggraph::ggraph(g, layout = "hive", axis = Type, sort.by = centrality) + + ggraph::geom_edge_hive(ggplot2::aes(colour = "gray", width = weight)) + + ggraph::geom_axis_hive(ggplot2::aes(colour = color), + size = 5, + label = TRUE) + + ggraph::scale_edge_color_identity() + + theme(legend.position = "none") + +# example with negative edge weights +nw <- dna_network(networkType = "onemode", + qualifierAggregation = "subtract", + excludeValues = list(concept = + "There should be legislation to regulate emissions.")) +g <- dna_tidygraph(nw, at) +ggraph::ggraph(g, layout = "linear", circular = TRUE) + + ggraph::geom_edge_arc(aes(color = color, width = abs)) + + ggraph::scale_edge_color_identity() + + ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + + ggplot2::scale_color_identity() + + ggraph::theme_graph(background = "white") + + theme(legend.position = "none") + + ggraph::geom_node_text(ggplot2::aes(label = name), + repel = TRUE, + max.overlaps = 10, + show.legend = FALSE) + +# example with a two-mode network +nw <- dna_network(networkType = "twomode", + qualifierAggregation = "combine") +at1 <- dna_getAttributes(statementTypeId = 1, variable = "organization") +at2 <- dna_getAttributes(statementTypeId = 1, variable = "concept") +at1$Notes <- "organization" +at2$Notes <- "concept" +at <- rbind(at1, at2) +g <- dna_tidygraph(nw, at) +ggraph::ggraph(g, layout = "graphopt") + + ggraph::geom_edge_link(ggplot2::aes(color = color), width = 1) + + ggraph::scale_edge_color_identity() + + ggraph::geom_node_point(ggplot2::aes(color = color, shape = Notes), + size = 5) + + ggplot2::scale_color_identity() + + ggraph::geom_node_text(ggplot2::aes(label = name), + repel = TRUE, + max.overlaps = 10, + show.legend = FALSE) + + ggraph::theme_graph(background = "white") + + theme(legend.position = "none") +} + +# manipulate and plot using the igraph package +library("igraph") +class(g) # resulting objects are both tbl_graph and igraph objects +igraph::V(g) # get the nodes using igraph functions +igraph::E(g) # get the edges using igraph functions +igraph::plot(g) # plot network using igraph package + +# convert to network object (network package, statnet suite of packages) +library("intergraph") +intergraph::asNetwork(g) + +} +\seealso{ +Other {rDNA networks}: +\code{\link{as.matrix.dna_network_onemode}()}, +\code{\link{as.matrix.dna_network_twomode}()}, +\code{\link{autoplot.dna_network}}, +\code{\link{dna_network}()}, +\code{\link{print.dna_network_onemode}()}, +\code{\link{print.dna_network_twomode}()} +} +\author{ +Philip Leifeld +} +\concept{{rDNA networks}} diff --git a/rDNA/rDNA/man/print.dna_network_onemode.Rd b/rDNA/rDNA/man/print.dna_network_onemode.Rd index 5e67e982..609f353e 100644 --- a/rDNA/rDNA/man/print.dna_network_onemode.Rd +++ b/rDNA/rDNA/man/print.dna_network_onemode.Rd @@ -33,6 +33,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_twomode}()} } \author{ diff --git a/rDNA/rDNA/man/print.dna_network_twomode.Rd b/rDNA/rDNA/man/print.dna_network_twomode.Rd index cafd3e31..b611cb67 100644 --- a/rDNA/rDNA/man/print.dna_network_twomode.Rd +++ b/rDNA/rDNA/man/print.dna_network_twomode.Rd @@ -33,6 +33,7 @@ Other {rDNA networks}: \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, +\code{\link{dna_tidygraph}()}, \code{\link{print.dna_network_onemode}()} } \author{ From 1ce2878de7a98a39474567da30ccef314670e852 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Sat, 10 Aug 2024 18:28:49 +0200 Subject: [PATCH 21/43] Fixed rDNA backbone help and CRAN checks for dna_tidygraph --- rDNA/rDNA/R/rDNA.R | 58 ++++++++++++++++++++++------------ rDNA/rDNA/man/dna_backbone.Rd | 47 ++++++++++++++++++--------- rDNA/rDNA/man/dna_tidygraph.Rd | 2 +- 3 files changed, 70 insertions(+), 37 deletions(-) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 14b49955..6d3e14dd 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -1963,7 +1963,6 @@ autoplot.dna_network_twomode <- autoplot.dna_network_onemode #' show.legend = FALSE) + #' ggraph::theme_graph(background = "white") + #' theme(legend.position = "none") -#' } #' #' # manipulate and plot using the igraph package #' library("igraph") @@ -1975,9 +1974,11 @@ autoplot.dna_network_twomode <- autoplot.dna_network_onemode #' # convert to network object (network package, statnet suite of packages) #' library("intergraph") #' intergraph::asNetwork(g) +#' } #' #' @author Philip Leifeld #' @family {rDNA networks} +#' @importFrom rlang .data #' @export dna_tidygraph <- function(network, attributes = NULL, ...) { if (length(intersect(c("dna_network_onemode", "dna_network_twomode", "matrix"), class(network))) < 1) { @@ -2039,9 +2040,9 @@ dna_tidygraph <- function(network, attributes = NULL, ...) { }) g <- g |> # assign absolute values, edge colors, and sign as edge attributes tidygraph::activate(edges) |> - tidygraph::mutate(abs = abs(weight), - color = edgecol, - sign = ifelse(weight < 0, "negative", "positive")) + tidygraph::mutate(abs = abs(.data$weight), + color = .data$edgecol, + sign = ifelse(.data$weight < 0, "negative", "positive")) return(g) } @@ -2587,9 +2588,38 @@ autoplot.dna_barplot <- function(object, #' #' Compute and retrieve the backbone and redundant set of a discourse network. #' -#' This function applies a simulated annealing algorithm to the discourse -#' network to partition the set of second-mode entities (e.g., concepts) into a -#' backbone set and a complementary redundant set. +#' The dna_backbone function applies a simulated annealing algorithm to the +#' discourse network to partition the set of second-mode entities (e.g., +#' concepts) into a backbone set and a complementary redundant set. Three +#' methods are available: +#' \itemize{ +#' \item A simulated annealing algorithm with a penalty. You can play with +#' different penalties and see how they affect the size of your backbone +#' set. +#' \item A modified simulated annealing algorithm for a fixed number of +#' backbone entities to retain. This is computationally simpler, but you +#' have to know how large the set should be. +#' \item A fast and greedy nested algorithm, which evaluates all possible +#' fixed backbone solutions, i.e., for all sizes, and provides a nested +#' hierarchy of entities on the second mode. This algorithm may stay below +#' the optimum and is only an approximation but provides insights into the +#' hierarchy of concepts and their relative importance. +#' } +#' +#' The \code{autoplot} function requires the ggplot2 package and can plot +#' algorithm diagnostics and the hierarchy of entities as a dendrogram, +#' depending on the method that was chosen. The \code{plot} function can do the +#' same thing, just using base plots, not ggplot2. +#' +#' The \code{dna_evaluateBackboneSolution} function computes the spectral loss +#' for an arbitrary backbone and its complement, the redundant set, specified by +#' the user. For example, the user can evaluate how much structure would be lost +#' if the second mode was composed only of the concepts provided to this +#' function. This can be used to compare how useful different codebook models +#' are. The penalty parameter \code{p} applies a penalty factor to the spectral +#' loss. The default value of \code{0} switches off the penalty as it is usually +#' not needed to evaluate a specific solution. The backbone set can be supplied +#' as a vector of character objects, for example concepts. #' #' @param method The backbone algorithm used to compute the results. Several #' methods are available: @@ -3192,18 +3222,6 @@ autoplot.dna_backbone <- function(object, ..., ma = 500) { } } -#' Evaluate the spectral loss for an arbitrary set of entities -#' -#' Compute the backbone loss for any set of entities, for example concepts. -#' -#' This function computes the spectral loss for an arbitrary backbone and its -#' complement, the redundant set, specified by the user. For example, the user -#' can evaluate how much structure would be lost if the second mode was composed -#' only of the concepts provided to this function. This can be used to compare -#' how useful different codebook models are. The penalty parameter \code{p} -#' applies a penalty factor to the spectral loss. The default value of \code{0} -#' switches off the penalty. -#' #' @param backboneEntities A vector of character values to be included in the #' backbone. The function will compute the spectral loss between the full #' network and the network composed only of those entities on the second mode @@ -3225,8 +3243,6 @@ autoplot.dna_backbone <- function(object, ..., ma = 500) { #' ) #' } #' -#' @author Philip Leifeld -#' #' @rdname dna_backbone #' @importFrom rJava .jarray #' @importFrom rJava .jcall diff --git a/rDNA/rDNA/man/dna_backbone.Rd b/rDNA/rDNA/man/dna_backbone.Rd index 128d460c..d5bae13d 100644 --- a/rDNA/rDNA/man/dna_backbone.Rd +++ b/rDNA/rDNA/man/dna_backbone.Rd @@ -319,21 +319,40 @@ A vector with two numeric values: the backbone and redundant loss. } \description{ Compute and retrieve the backbone and redundant set of a discourse network. - -Compute the backbone loss for any set of entities, for example concepts. } \details{ -This function applies a simulated annealing algorithm to the discourse -network to partition the set of second-mode entities (e.g., concepts) into a -backbone set and a complementary redundant set. - -This function computes the spectral loss for an arbitrary backbone and its -complement, the redundant set, specified by the user. For example, the user -can evaluate how much structure would be lost if the second mode was composed -only of the concepts provided to this function. This can be used to compare -how useful different codebook models are. The penalty parameter \code{p} -applies a penalty factor to the spectral loss. The default value of \code{0} -switches off the penalty. +The dna_backbone function applies a simulated annealing algorithm to the +discourse network to partition the set of second-mode entities (e.g., +concepts) into a backbone set and a complementary redundant set. Three +methods are available: +\itemize{ + \item A simulated annealing algorithm with a penalty. You can play with + different penalties and see how they affect the size of your backbone + set. + \item A modified simulated annealing algorithm for a fixed number of + backbone entities to retain. This is computationally simpler, but you + have to know how large the set should be. + \item A fast and greedy nested algorithm, which evaluates all possible + fixed backbone solutions, i.e., for all sizes, and provides a nested + hierarchy of entities on the second mode. This algorithm may stay below + the optimum and is only an approximation but provides insights into the + hierarchy of concepts and their relative importance. +} + +The \code{autoplot} function requires the ggplot2 package and can plot +algorithm diagnostics and the hierarchy of entities as a dendrogram, +depending on the method that was chosen. The \code{plot} function can do the +same thing, just using base plots, not ggplot2. + +The \code{dna_evaluateBackboneSolution} function computes the spectral loss +for an arbitrary backbone and its complement, the redundant set, specified by +the user. For example, the user can evaluate how much structure would be lost +if the second mode was composed only of the concepts provided to this +function. This can be used to compare how useful different codebook models +are. The penalty parameter \code{p} applies a penalty factor to the spectral +loss. The default value of \code{0} switches off the penalty as it is usually +not needed to evaluate a specific solution. The backbone set can be supplied +as a vector of character objects, for example concepts. } \examples{ \dontrun{ @@ -423,6 +442,4 @@ dna_evaluateBackboneSolution( } \author{ Philip Leifeld, Tim Henrichsen - -Philip Leifeld } diff --git a/rDNA/rDNA/man/dna_tidygraph.Rd b/rDNA/rDNA/man/dna_tidygraph.Rd index a64238b5..e695a2ac 100644 --- a/rDNA/rDNA/man/dna_tidygraph.Rd +++ b/rDNA/rDNA/man/dna_tidygraph.Rd @@ -121,7 +121,6 @@ ggraph::ggraph(g, layout = "graphopt") + show.legend = FALSE) + ggraph::theme_graph(background = "white") + theme(legend.position = "none") -} # manipulate and plot using the igraph package library("igraph") @@ -133,6 +132,7 @@ igraph::plot(g) # plot network using igraph package # convert to network object (network package, statnet suite of packages) library("intergraph") intergraph::asNetwork(g) +} } \seealso{ From e926c3859d4135ebae46b4494fb0487c419f3480 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Sat, 17 Aug 2024 19:51:38 +0200 Subject: [PATCH 22/43] Ported IRT scaling from rDNA 2 to rDNA 3 --- rDNA/rDNA/DESCRIPTION | 7 +- rDNA/rDNA/NAMESPACE | 8 + rDNA/rDNA/R/dna_scale.R | 1695 +++++++++++++++++++++++ rDNA/rDNA/man/dna_scaling.Rd | 631 +++++++++ rDNA/rDNA/tests/testthat/test-scaling.R | 192 +++ 5 files changed, 2531 insertions(+), 2 deletions(-) create mode 100644 rDNA/rDNA/R/dna_scale.R create mode 100644 rDNA/rDNA/man/dna_scaling.Rd create mode 100644 rDNA/rDNA/tests/testthat/test-scaling.R diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 8320c6a0..33a3623a 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-08-10 +Date: 2024-08-17 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", @@ -39,7 +39,10 @@ Suggests: MASS (>= 7.3-51.5), factoextra (>= 1.0.7), heatmaply (>= 1.4.2), - tidygraph (>= 1.3.1) + tidygraph (>= 1.3.1), + ggrepel (>= 0.9.5), + MCMCpack (>= 1.7-0), + coda (>= 0.19-4.1) SystemRequirements: Java (>= 11) License: GPL-2 URL: https://github.com/leifeld/dna diff --git a/rDNA/rDNA/NAMESPACE b/rDNA/rDNA/NAMESPACE index 20b7bafa..a0ceb384 100644 --- a/rDNA/rDNA/NAMESPACE +++ b/rDNA/rDNA/NAMESPACE @@ -7,6 +7,7 @@ S3method(autoplot,dna_barplot) S3method(autoplot,dna_network_onemode) S3method(autoplot,dna_network_twomode) S3method(autoplot,dna_phaseTransitions) +S3method(autoplot,dna_scale) S3method(plot,dna_backbone) S3method(print,dna_backbone) S3method(print,dna_barplot) @@ -14,6 +15,7 @@ S3method(print,dna_multiclust) S3method(print,dna_network_onemode) S3method(print,dna_network_twomode) S3method(print,dna_phaseTransitions) +S3method(print,dna_scale) export(dna_api) export(dna_backbone) export(dna_barplot) @@ -33,6 +35,10 @@ export(dna_printDetails) export(dna_queryCoders) export(dna_sample) export(dna_saveConnectionProfile) +export(dna_scale1dbin) +export(dna_scale1dord) +export(dna_scale2dbin) +export(dna_scale2dord) export(dna_tidygraph) importFrom(ggplot2,.pt) importFrom(ggplot2,aes) @@ -83,6 +89,8 @@ importFrom(stats,dist) importFrom(stats,filter) importFrom(stats,hclust) importFrom(stats,kmeans) +importFrom(stats,reorder) +importFrom(stats,reshape) importFrom(utils,combn) importFrom(utils,download.file) importFrom(utils,head) diff --git a/rDNA/rDNA/R/dna_scale.R b/rDNA/rDNA/R/dna_scale.R new file mode 100644 index 00000000..d0a8a47c --- /dev/null +++ b/rDNA/rDNA/R/dna_scale.R @@ -0,0 +1,1695 @@ +#' Ideological scaling for discourse networks using item response theory +#' +#' One- or two-dimensional binary or ordinal IRT scaling for DNA. +#' +#' @section Overview: +#' This set of functions applies item response theory (IRT) to discourse +#' networks to scale actors and concepts on an underlying ideological scale. +#' Four estimation functions are available: +#' \itemize{ +#' \item \code{dna_scale1dbin} Binary scaling in one dimension. +#' \item \code{dna_scale2dbin} Binary scaling in two dimensions. +#' \item \code{dna_scale1dord} Ordinal scaling in one dimension. +#' \item \code{dna_scale2dord} Ordinal scaling in two dimensions. +#' } +#' These functions are convenience wrappers for the \code{MCMCirt1d}, +#' \code{MCMCirtKd}, and \code{MCMCordfactanal} functions in the MCMCpack +#' package, which use Markov Chain Monte Carlo (MCMC) methods to generate +#' posterior samples of ability, discrimination, and difficulty parameters. +#' +#' The corresponding \code{print} function prints the formatted posterior means +#' and HPD intervals to the console. The autoplot function plots the MCMC trace, +#' density, and summaries of estimates with the help of the \pkg{ggplot2} +#' package. +#' +#' @section Model interpretation: +#' One parameter for each actor and two parameters for each concept are +#' estimated: +#' \itemize{ +#' \item Ability parameter: The estimated ability parameter indicates an +#' actor's ideological position on a left-right scale (e.g., industry vs. +#' environment, labor vs. capital, containing vs. expanding regulation etc.) +#' or on two dimensions. It is denoted as theta (\eqn{\theta}) in the binary +#' model and phi (\eqn{\phi}) in the ordinal model in this implementation. +#' \item Discrimination parameter: The estimated discrimination parameter +#' indicates a concept's ability to discriminate between actors on their +#' left-right scale. The discrimination parameter measures how well an item +#' can distinguish between actors with different levels of the latent trait +#' (in this case, ideology). It reflects how strongly the item is related +#' to the underlying trait being measured. A high discrimination parameter +#' means that the item is very effective at differentiating between +#' individuals who have slightly different levels of the underlying +#' ideology. For instance, a highly discriminatory question will sharply +#' differentiate between actors on either side of an ideological spectrum +#' (e.g., conservative vs. liberal). A higher discrimination value indicates +#' that the item is more sensitive to changes in the latent trait +#' (ideology). A low discrimination value suggests that the item does not +#' differentiate well between actors with different levels of ideology. In +#' the notation used in this implementation, the discrimination parameter is +#' denoted as beta (\eqn{\beta}) in the binary model and Lambda 2 +#' (\eqn{\Lambda_2}) in the ordinal model, though elsewhere in the literature +#' it is often denoted as alpha (\eqn{\alpha}). +#' \item Difficulty parameter: The difficulty parameter for a concept +#' represents the location of the item along the ideological spectrum. The +#' difficulty parameter helps determine where on the ideological spectrum a +#' particular item is situated. For instance, an item with a high difficulty +#' parameter might represent a position that only those with a +#' strong ideological stance (e.g., very liberal or very conservative) are +#' likely to endorse. An item with a low difficulty parameter would be +#' endorsed by most actors, indicating that the statement is relatively easy +#' to agree with (possibly a moderate or widely accepted stance). +#' Conversely, an item with a high difficulty parameter would only be +#' endorsed by those with a more extreme position on the ideology being +#' measured. In the notation used in this implementation, the difficulty +#' parameter is denoted as alpha (\eqn{\alpha}) in the binary model and as +#' Lambda 1 (\eqn{\Lambda_1}) in the ordinal model, though elsewhere in the +#' literature it is often denoted as beta (\eqn{\beta}). +#' } +#' See the help pages of the \code{MCMCirt1d} function (for the binary model) +#' and the \code{MCMCordfactanal} (for the ordinal model) for details on the +#' functional form and parameterization of the mdoel, which is a slight +#' deviation from the standard 2PL model. +#' +#' @section Variable coding: +#' As in a two-mode network in \link{dna_network}, two variables have to be +#' provided for the scaling. The first variable corresponds to the rows of a +#' two-mode network and usually entails actors (e.g., \code{"organizations"}), +#' while the second variable is equal to the columns of a two-mode network, +#' typically expressed by \code{"concepts"}. The \code{dna_scale} functions +#' use \code{"actors"} and \code{"concepts"} as synonyms for \code{variable1} +#' and \code{variable2}. However, the scaling is not restricted to +#' \code{"actors"} and \code{"concepts"} but depends on what you provide in +#' \code{variable1} or \code{variable2}. +#' +#' @section Binary models: +#' Binary models recode two-mode network matrices into zeroes and ones and then +#' estimate a logit model for binary data. The network cells are recoded using +#' the following rules: +#' \itemize{ +#' \item For a binary qualifier, \code{dna_scale1dbin} internally uses the +#' \code{combine} qualifier aggregation and then recodes the values into +#' \code{0} for disagreement, \code{1} for agreement, and \code{NA} for +#' mixed positions and non-mentions of concepts. If +#' \code{zero_as_na = FALSE} is set, the mixed positions and non-mentions +#' become \code{0} instead of \code{NA} and are treated as informative. +#' \item If no qualifier is used or the qualifier variable is categorical, +#' non-mentions become \code{0} and any number of mentions become \code{1}. +#' \code{zero_as_na} must be \code{FALSE} in this case. +#' \item If a threshold is used (e.g., \code{0.4}), the fraction of positive +#' mentions over the sum of both positive and negative mentions, which +#' scales between \code{0} and \code{1}, is used to recode fractions smaller +#' than or equal to the threshold as \code{0}, values larger than or equal +#' to one minus the threshold as \code{1}, and values between the threshold +#' and one minus the threshold as \code{NA}. For example, if an actor +#' mentions a concept six times in a positive way and five times in a +#' negative way, the fraction of positive mentions is \code{6 / 11 = 0.54}. +#' If a threshold of \code{0.4} is used (or, equivalently, \code{0.6}), the +#' value is recoded to \code{NA}. If no threshold is used, the value becomes +#' \code{1}. +#' \item Integer qualifiers are also recoded into \code{0} and \code{1} by +#' rescaling the qualifier values between \code{0} and \code{1}. Thresholds +#' larger than \code{0} and smaller than \code{1} are possible here as well. +#' } +#' +#' @section Ordinal models: +#' Ordinal models recode two-mode network matrices into values \code{1}, +#' \code{2}, or \code{3} and then estimate an ordinal latent factor model for +#' ordinal data, which corresponds to ordinal item response theory. The network +#' cells are recoded using the following rules: +#' \itemize{ +#' \item For a binary qualifier, \code{dna_scale1dord} internally uses the +#' \code{combine} qualifier aggregation and then recodes the values into +#' \code{1} for disagreement, \code{2} for ambivalent positions with both +#' positive and negative mentions, \code{3} for exclusively positive +#' mentions, and \code{NA} for non-mentions. If \code{zero_as_na = FALSE} is +#' set, non-mentions are recoded as \code{2} as well and thereby become +#' informative as neutral positions. +#' \item If no qualifier is used or the qualifier variable is categorical, +#' non-mentions become \code{0} and any number of mentions become \code{1}. +#' \code{zero_as_na} must be \code{FALSE} in this case. +#' \item If a threshold is used, the same recoding procedure as in the binary +#' model is used, but values below or equal to the threshold become +#' \code{1}, values above the threshold and below one minus the threshold +#' become \code{2}, values equal to or above the threshold become \code{3}, +#' and non-mentions are coded as \code{NA} (unless +#' \code{zero_as_na = FALSE}). +#' \item Integer qualifiers are also recoded into three positive integer +#' values by rescaling the qualifier values between \code{0} and \code{1}. +#' Thresholds larger than \code{0} and smaller than \code{1} are possible +#' here as well. +#' } +#' In ordinal models, threshold parameters are estimated alongside the other +#' parameters, like in other ordinal logit models. They are treated as nuisance +#' parameters and not reported in the output. However, they are stored in the +#' object as part of the posterior samples and can be retrieved if necessary. +#' +#' @section Tweaking the estimation: +#' As these functions implement a Bayesian Item Response Theory approach, +#' \code{priors} and \code{starting values} can be set on the actor and concept +#' parameters. Changing the default \code{prior} values can often help you to +#' achieve better results. Constraints on the actor parameters can also be +#' specified to help identifying the model and to indicate in which direction +#' ideological positions of actors and concepts run. The returned MCMC output +#' can also be post-processed by normalizing the samples for each iteration with +#' \code{mcmc_normalize}. Normalization can be a sufficient way of identifying +#' one-dimensional ideal point models. +#' +#' Unlike \link{dna_scale1dbin}, \link{dna_scale2dbin} constrains the values +#' indicated in \code{variable2}. For these values, the scaling estimates an +#' item discrimination parameter for each dimension and an item difficulty +#' parameter for both dimensions. The item difficulty parameter should, +#' however, not be constrained (see \link[MCMCpack]{MCMCirtKd}). Therefore, you +#' should set constraints on the item discrimination parameters. +#' +#' Fitting two-dimensional scaling models requires a good choice of concept +#' constraints to specify the ideological dimensions of your data. A suitable +#' way of identifying your ideological dimensions is to constrain one item +#' discrimination parameter to load only on one dimension. This means that we +#' set one parameter to load either positive or negative on one dimension and +#' setting it to zero on the other. A second concept should also be constrained +#' to load either positive or negative on one dimension (see example). +#' +#' The argument \code{drop_min_actors} excludes actors with only a limited +#' number of concepts used. Limited participation of actors in a debate can +#' impact the scaling of the ideal points, as actors with only few mentions of +#' concepts convey limited information on their ideological position. The same +#' can also be done for concepts with the argument \code{drop_min_concepts}. +#' Concepts that have been rarely mentioned do not strongly discriminate the +#' ideological positions of actors and can, therefore, impact the accuracy of +#' the scaling. Reducing the number of actors of concepts to be scaled hence +#' improves the precision of the ideological positions for both variables and +#' the scaling itself. Another possibility to reduce the number of concepts is +#' to use \code{drop_constant_concepts}, which will reduce concepts not having +#' any variation in the agreement/disagreement structure of actors. This means +#' that all concepts will be dropped which have only agreeing or disagreeing +#' statements. +#' +#' @param statementType The statement type as a character object. +#' @param variable1 The first variable for the scaling construction (see +#' \link{dna_network}). Defaults to \code{"organization"}. +#' @param variable2 The second variable for the scaling construction (see +#' \link{dna_network}). Defaults to \code{"concept"}. +#' @param qualifier The qualifier variable for the scaling construction (see +#' \link{dna_network}). Defaults to \code{"agreement"}. If you did not use +#' a qualifier in the coding, you can set the qualifier to be the same value +#' as variable2 and estimate an ordinal model with argument +#' \code{zero_as_na = FALSE} (because non-mentions of concepts are interpreted +#' as \code{NA} in the binary model). +#' @param zero_as_na Logical. Only ordinal models. If \code{TRUE}, all +#' non-mentions of an actor towards a concept will be recoded as \code{NA}. If +#' \code{FALSE} as \code{2}. +#' @param threshold Numeric value that specifies when a mixed position can be +#' considered as agreement or disagreement. If, for example, one actor has 60 +#' percent of agreeing and 40 percent of disagreeing statements towards a +#' concept, a \code{threshold} of 0.51 will recode the actor position on this +#' concept as "agreement". The same accounts also for disagreeing statements. +#' If one actor has 60 percent of disagreeing and 40 percent of agreeing +#' statements, a \code{threshold} of 0.51 will recode the actor position on +#' this concept as "disagreement". All values in between the \code{threshold} +#' (e.g., 55 percent agreement and 45 percent of disagreement and a threshold +#' of 0.6) will be recoded as \code{NA}. If is set to \code{NULL}, all "mixed" +#' positions of actors will be recoded as \code{NA}. Must be strictly +#' positive. +#' @param theta_constraints A list specifying the constraints on the actor +#' parameter in a one-dimensional binary model. Three forms of constraints are +#' possible: \code{actorname = value}, which will constrain an actor to be +#' equal to the specified value (e.g. \code{0}), \code{actorname = "+"}, which +#' will constrain the actor to be positively scaled and +#' \code{actorname = "-"}, which will constrain the actor to be negatively +#' scaled (see example). +#' @param lambda_constraints A list of lists specifying constraints on the +#' concept parameters in an ordinal model. Note that value \code{1} in the +#' brackets of the argument refers to the negative item difficulty parameters, +#' which in general should not be constrained. Value \code{2} relates to the +#' item discrimination parameter and should be used for constraints on +#' concepts. Three forms of constraints are possible: +#' \code{conceptname = list(2, value)} will constrain the item discrimination +#' parameter to be equal to the specified value (e.g., 0). +#' \code{conceptname = list(2,"+")} will constrain the item discrimination +#' parameter to be positively scaled and \code{conceptname = list(2, "-")} +#' will constrain the parameter to be negatively scaled (see example). +#' @param item_constraints A list of lists specifying constraints on the +#' concept parameters in a two-dimensional binary model. Note that value +#' \code{1} in the brackets of the argument refers to the item difficulty +#' parameters, which in general should not be constrained. All values above +#' \code{1} relate to the item discrimination parameters on the single +#' dimensions. These should be used for constraints on concepts. Three forms +#' of constraints are possible: \code{conceptname = list(2, value)} will +#' constrain a concept to be equal to the specified value (e.g., 0) on the +#' first dimension of the item discrimination parameter. +#' \code{conceptname = list(2,"+")} will constrain the concept to be +#' positively scaled on the first dimension and +#' \code{conceptname = list(2, "-")} will constrain the concept to be +#' negatively scaled on the first dimension (see example). If you +#' wish to constrain a concept on the second dimension, please indicate this +#' with a \code{3} in the first position in the bracket. +#' @param mcmc_iterations The number of iterations for the sampler (not +#' including the burn-in iterations, which are discarded) before thinning. +#' @param mcmc_burnin The number of burn-in iterations for the sampler, which +#' are discarded. +#' @param mcmc_thin The thinning interval for the sampler. Iterations must be +#' divisible by the thinning interval. The final number of samples retained in +#' the output equals \code{(mcmc_iterations - mcmc_burnin) / mcmc_thin}. +#' @param mcmc_tune Only ordinal models. The tuning parameter for the acceptance +#' rates of the sampler. Acceptance rates should ideally range between +#' \code{0.15} and \code{0.5}. Can be either a scalar or a k-vector. Must be +#' strictly positive. +#' @param mcmc_normalize Logical. Should the MCMC output be normalized? If +#' \code{TRUE}, samples are normalized to a mean of \code{0} and a standard +#' deviation of \code{1}. +#' @param theta_start The starting values for the actor parameters in a +#' one-dimensional binary model. Can either be a scalar or a column vector +#' with as many elements as the number of actors included in the scaling. If +#' set to the default \code{NA}, starting values will be set according to an +#' eigenvalue-eigenvector decomposition of the actor agreement score. +#' @param alpha_start The starting values for the concept difficulty +#' parameters in a one-dimensional binary model. Can either be a scalar or a +#' column vector with as many elements as the number of items included in the +#' scaling. If set to the default \code{NA}, starting values will be set +#' according to a series of probit regressions that condition the starting +#' values of the difficulty parameters. +#' @param beta_start The starting values for the concept discrimination +#' parameters in a one-dimensional binary model. Can either be a scalar or a +#' column vector with as many elements as the number of items included in the +#' scaling. If set to the default \code{NA}, starting values will be set +#' according to a series of probit regressions that condition the starting +#' values of the discrimination parameters. +#' @param alpha_beta_start The starting values for the concept difficulty and +#' discrimination parameters in a two-dimensional binary model. Can either be +#' a scalar or a column vector with as many elements as the number of items +#' included in the scaling. If set to the default \code{NA}, starting values +#' will be set according to a series of probit regressions that condition the +#' starting values of the difficulty and discrimination parameters. +#' @param lambda_start The starting values for the concept discrimination +#' parameters in an ordinal model. Can be either a scalar or a matrix. If set +#' to \code{NA} (default), the \code{starting values} for the unconstrained +#' parameters in the first column are based on the observed response pattern. +#' The remaining unconstrained elements are set to \code{starting values} of +#' either \code{1.0} or \code{-1.0}, depending on the nature of the +#' constraint. +#' @param theta_prior_mean A scalar value specifying the prior mean of the +#' actor parameters in a one-dimensional binary model. +#' @param theta_prior_variance A scalar value specifying the prior inverse +#' variances of the actor parameters in a one-dimensional binary model. +#' @param alpha_beta_prior_mean Mean of the difficulty and discrimination +#' parameters in a one- or two-dimensional binary model. Can either be a +#' scalar or a vector of length two. If a scalar, both means will be set +#' according to the specified value. +#' @param alpha_beta_prior_variance Inverse variance of the difficulty and +#' discrimination parameters in a one- or two-dimensional binary model. Can +#' either be a scalar or a vector of length two. If a scalar, both means will +#' be set according to the specified value. +#' @param lambda_prior_mean The prior mean of the concept discrimination +#' parameters in an ordinal model. Can be either a scalar or a matrix. +#' @param lambda_prior_variance The prior inverse variances of the concept +#' discrimination parameters in an ordinal model. Can be either a scalar or a +#' matrix. +#' @param store_variables A character vector indicating which variables should +#' be stored from the scaling. Can either take the value of the character +#' vector indicated in \code{variable1} or \code{variable2} or \code{"both"} +#' to store both variables. Note that saving both variables can impact the +#' speed of the scaling. Defaults to \code{"both"}. +#' @param drop_constant_concepts Logical. Should concepts that have no +#' variation be deleted before the scaling? Defaults to \code{FALSE}. +#' @param drop_min_actors A numeric value specifying the minimum number of +#' concepts actors should have mentioned to be included in the scaling. +#' Defaults to \code{1}. +#' @param drop_min_concepts A numeric value specifying the minimum number a +#' concept should have been jointly mentioned by actors. Defaults to \code{2}. +#' @param verbose A boolean or numeric value indicating whether the iterations +#' of the scaling should be printed to the R console. If set to a numeric +#' value, every \code{verboseth} iteration will be printed. If set to +#' \code{TRUE}, \code{verbose} will print the total of iterations and burn-in +#' divided by \code{10}. +#' @param seed The random seed for the scaling. +#' @param ... Additional arguments passed to \link{dna_network}. Actors can, for +#' example, be removed with the \code{excludeValues} arguments. The scaling +#' can also be applied to a specific time slice by using \code{start.date} and +#' \code{stop.date}. For the \code{autoplot} method, this argument is not in +#' use. +#' +#' @examples +#' \dontrun{ +#' library("rDNA") +#' library("ggplot2") +#' library("ggrepel") +#' +#' dna_init() +#' dna_openDatabase(dna_sample(overwrite = TRUE), coderPassword = "sample") +#' +#' # one-dimensional binary model +#' fit_1d_bin <- dna_scale1dbin( +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' threshold = 0.49, +#' theta_constraints = list( +#' `National Petrochemical & Refiners Association` = "+", +#' `Alliance to Save Energy` = "-"), +#' mcmc_iterations = 20000, +#' mcmc_burnin = 2000, +#' mcmc_thin = 10, +#' mcmc_normalize = TRUE, +#' theta_prior_mean = 0, +#' theta_prior_variance = 1, +#' alpha_beta_prior_mean = 0, +#' alpha_beta_prior_variance = 0.25, +#' store_variables = "both", +#' drop_constant_concepts = FALSE, +#' drop_min_actors = 1, +#' verbose = TRUE, +#' seed = 12345 +#' ) +#' fit_1d_bin +#' autoplot(fit_1d_bin) +#' +#' # two-dimensional binary model +#' fit_2d_bin <- dna_scale2dbin( +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' threshold = 0.4, +#' item_constraints = list( +#' `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), +#' `Climate change is caused by greenhouse gases (CO2).` = c(3, 0), +#' `CO2 legislation will not hurt the economy.` = list(3, "-")), +#' mcmc_iterations = 20000, +#' mcmc_burnin = 2000, +#' mcmc_thin = 10, +#' alpha_beta_prior_mean = 0, +#' alpha_beta_prior_variance = 1, +#' store_variables = "organization", +#' drop_constant_concepts = TRUE, +#' verbose = TRUE, +#' seed = 12345 +#' ) +#' fit_2d_bin +#' autoplot(fit_2d_bin) +#' +#' # one-dimensional ordinal model +#' fit_1d_ord <- dna_scale1dord( +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' zero_as_na = TRUE, +#' threshold = 0.4, +#' lambda_constraints = list(`CO2 legislation will not hurt the economy.` = list(2, "-")), +#' mcmc_iterations = 20000, +#' mcmc_burnin = 2000, +#' mcmc_thin = 10, +#' mcmc_tune = 1.5, +#' mcmc_normalize = FALSE, +#' lambda_prior_mean = 0, +#' lambda_prior_variance = 0.1, +#' store_variables = "organization", +#' drop_constant_concepts = TRUE, +#' verbose = TRUE, +#' seed = 12345 +#' ) +#' fit_1d_ord +#' autoplot(fit_1d_ord) +#' +#' # two-dimensional ordinal model +#' fit_2d_ord <- dna_scale2dord( +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' zero_as_na = TRUE, +#' threshold = 0.4, +#' lambda_constraints = list( +#' `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), +#' `Climate change is caused by greenhouse gases (CO2).` = list(3, 0), +#' `CO2 legislation will not hurt the economy.` = list(3, "-")), +#' mcmc_iterations = 20000, +#' mcmc_burnin = 2000, +#' mcmc_thin = 10, +#' mcmc_tune = 1.5, +#' lambda_prior_mean = 0, +#' lambda_prior_variance = 0.1, +#' store_variables = "both", +#' drop_constant_concepts = TRUE, +#' verbose = TRUE, +#' seed = 12345 +#' ) +#' fit_2d_ord +#' autoplot(fit_2d_ord) +#' } +#' +#' @author Tim Henrichsen, Philip Leifeld, Johannes B. Gruber +#' +#' @rdname dna_scaling +#' @export +dna_scale1dbin <- function(statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + theta_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_normalize = FALSE, + theta_start = NA, + alpha_start = NA, + beta_start = NA, + theta_prior_mean = 0, + theta_prior_variance = 1, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.25, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ...) { + + if (!requireNamespace("MCMCpack", quietly = TRUE)) { + stop("The DNA scaling functions require the 'MCMCpack' package to be installed.\n", + "To do this, enter 'install.packages(\"MCMCpack\")'.") + } + if (!requireNamespace("coda", quietly = TRUE)) { + stop("The DNA scaling functions require the 'coda' package to be installed.\n", + "To do this, enter 'install.packages(\"coda\")'.") + } + + dots <- list(...) + out <- bin_recode(statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + zero_as_na = zero_as_na, + threshold = threshold, + drop_min_actors = drop_min_actors, + drop_min_concepts = drop_min_concepts, + store_variables = store_variables, + dots = dots) + nw2 <- out$nw2 + dots <- out$dots + dots_nw <- out$dots_nw + invertValues <- out$invertValues + excludeValues <- out$excludeValues + # Scaling + x <- do.call(eval(parse(text = "MCMCpack::MCMCirt1d")), c(list( + nw2, + theta.constraints = theta_constraints, + burnin = mcmc_burnin, + mcmc = mcmc_iterations, + thin = mcmc_thin, + verbose = ifelse(verbose == TRUE, + ((mcmc_iterations + mcmc_burnin) / 10), + verbose), + seed = seed, + theta.start = theta_start, + alpha.start = alpha_start, + beta.start = beta_start, + t0 = theta_prior_mean, + T0 = theta_prior_variance, + ab0 = alpha_beta_prior_mean, + AB0 = alpha_beta_prior_variance, + store.item = (store_variables == variable2 | store_variables == "both"), + store.ability = (store_variables == variable1 | store_variables == "both"), + drop.constant.items = drop_constant_concepts), + dots)) + if (mcmc_normalize) { + names <- colnames(x) + x <- coda::as.mcmc(t(apply(x, 1, scale))) + colnames(x) <- names + } + dna_scale <- list() + dna_scale$sample <- x + # Store actor frequency for possible min argument in dna_plotScale + nw_freq <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (store_variables == variable1 | store_variables == "both") { + actors <- x[, grepl("^theta.", colnames(x))] + hpd <- as.data.frame(coda::HPDinterval(actors, prob = 0.95)) + actors <- as.data.frame(colMeans(actors)) + actors <- merge(actors, hpd, by = 0) + colnames(actors)[colnames(actors) == "colMeans(actors)"] <- "mean" + colnames(actors)[colnames(actors) == "lower"] <- "HPD2.5" + colnames(actors)[colnames(actors) == "upper"] <- "HPD97.5" + actors$Row.names <- gsub("^theta.", "", actors$Row.names) + at <- dna_getAttributes(statementType = statementType, + variable = variable1) + at <- at[at$value %in% actors$Row.names, ] + at$frequency <- rowSums(nw_freq)[match(at$value, rownames(nw_freq))] + actors <- merge(actors, at, by.x = "Row.names", by.y = "value") + actors <- actors[, !(colnames(actors) == "ID")] + dna_scale$ability <- actors + } + if (store_variables == variable2 | store_variables == "both") { + at <- dna_getAttributes(statementType = statementType, + variable = variable2) + concepts_beta <- x[, grepl("^beta.", colnames(x))] # beta = discrimination parameters + hpd_beta <- as.data.frame(coda::HPDinterval(concepts_beta, prob = 0.95)) + concepts_beta <- as.data.frame(colMeans(concepts_beta)) + concepts_beta <- merge(concepts_beta, hpd_beta, by = 0) + colnames(concepts_beta)[colnames(concepts_beta) == "colMeans(concepts_beta)"] <- "mean" + colnames(concepts_beta)[colnames(concepts_beta) == "lower"] <- "HPD2.5" + colnames(concepts_beta)[colnames(concepts_beta) == "upper"] <- "HPD97.5" + concepts_beta$Row.names <- gsub("^beta.", "", concepts_beta$Row.names) + at <- at[at$value %in% concepts_beta$Row.names, ] + concepts_beta <- merge(concepts_beta, at, by.x = "Row.names", by.y = "value") + concepts_beta <- concepts_beta[, !(colnames(concepts_beta) == "ID")] + dna_scale$discrimination <- concepts_beta + + concepts_alpha <- x[, grepl("^alpha.", colnames(x))] # alpha = difficulty parameters + hpd_alpha <- as.data.frame(coda::HPDinterval(concepts_alpha, prob = 0.95)) + concepts_alpha <- as.data.frame(colMeans(concepts_alpha)) + concepts_alpha <- merge(concepts_alpha, hpd_alpha, by = 0) + colnames(concepts_alpha)[colnames(concepts_alpha) == "colMeans(concepts_alpha)"] <- "mean" + colnames(concepts_alpha)[colnames(concepts_alpha) == "lower"] <- "HPD2.5" + colnames(concepts_alpha)[colnames(concepts_alpha) == "upper"] <- "HPD97.5" + concepts_alpha$Row.names <- gsub("^alpha.", "", concepts_alpha$Row.names) + at <- at[at$value %in% concepts_alpha$Row.names, ] + concepts_alpha <- merge(concepts_alpha, at, by.x = "Row.names", by.y = "value") + concepts_alpha <- concepts_alpha[, !(colnames(concepts_alpha) == "ID")] + dna_scale$difficulty <- concepts_alpha + + concepts <- rbind(concepts_beta, concepts_alpha) + } + dna_scale$call <- mget(names(formals()), sys.frame(sys.nframe())) + dna_scale$call$connection <- NULL + class(dna_scale) <- c("dna_scale1dbin", class(dna_scale)) + class(dna_scale) <- c("dna_scale", class(dna_scale)) + return(dna_scale) +} + +#' @rdname dna_scaling +#' @export +dna_scale1dord <- function(statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + lambda_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_tune = 1.5, + mcmc_normalize = FALSE, + lambda_start = NA, + lambda_prior_mean = 0, + lambda_prior_variance = 1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ...) { + + if (!requireNamespace("MCMCpack", quietly = TRUE)) { + stop("The DNA scaling functions require the 'MCMCpack' package to be installed.\n", + "To do this, enter 'install.packages(\"MCMCpack\")'.") + } + if (!requireNamespace("coda", quietly = TRUE)) { + stop("The DNA scaling functions require the 'coda' package to be installed.\n", + "To do this, enter 'install.packages(\"coda\")'.") + } + + dots <- list(...) + out <- ord_recode(statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + zero_as_na = zero_as_na, + threshold = threshold, + drop_min_actors = drop_min_actors, + drop_min_concepts = drop_min_concepts, + store_variables = store_variables, + dots = dots) + nw2 <- out$nw2 + dots <- out$dots + dots_nw <- out$dots_nw + invertValues <- out$invertValues + excludeValues <- out$excludeValues + # Scaling + x <- do.call(eval(parse(text = "MCMCpack::MCMCordfactanal")), c(list( + nw2, + factors = 1, + lambda.constraints = lambda_constraints, + burnin = mcmc_burnin, + mcmc = mcmc_iterations, + thin = mcmc_thin, + tune = mcmc_tune, + verbose = ifelse(verbose == TRUE, + ((mcmc_iterations + mcmc_burnin) / 10), + verbose), + seed = seed, + lambda.start = lambda_start, + l0 = lambda_prior_mean, + L0 = lambda_prior_variance, + store.lambda = (store_variables == variable2 | store_variables == "both"), + store.scores = (store_variables == variable1 | store_variables == "both"), + drop.constantvars = drop_constant_concepts), + dots)) + if (mcmc_normalize) { + names <- colnames(x) + x <- coda::as.mcmc(t(apply(x, 1, scale))) + colnames(x) <- names + } + dna_scale <- list() + dna_scale$sample <- x + # Store actor frequency for possible min argument in dna_plotScale + nw_freq <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (store_variables == variable1 | store_variables == "both") { + actors <- x[, grepl("^phi.", colnames(x))] + hpd <- as.data.frame(coda::HPDinterval(actors, prob = 0.95)) + actors <- as.data.frame(colMeans(actors)) + actors <- merge(actors, hpd, by = 0) + colnames(actors)[colnames(actors) == "colMeans(actors)"] <- "mean" + colnames(actors)[colnames(actors) == "lower"] <- "HPD2.5" + colnames(actors)[colnames(actors) == "upper"] <- "HPD97.5" + actors$Row.names <- gsub("^phi.|.2$", "", actors$Row.names) + at <- dna_getAttributes(statementType = statementType, + variable = variable1) + at <- at[at$value %in% actors$Row.names, ] + at$frequency <- rowSums(nw_freq)[match(at$value, rownames(nw_freq))] + actors <- merge(actors, at, by.x = "Row.names", by.y = "value") + actors <- actors[, !(colnames(actors) == "ID")] + dna_scale$ability <- actors + } + if (store_variables == variable2 | store_variables == "both") { + at <- dna_getAttributes(statementType = statementType, variable = variable2) + concepts_lambda <- x[, grepl("^Lambda.", colnames(x))] # Lambda = difficulty and discrimination parameters + hpd_lambda <- as.data.frame(coda::HPDinterval(concepts_lambda, prob = 0.95)) + concepts_lambda <- as.data.frame(colMeans(concepts_lambda)) + concepts_lambda <- merge(concepts_lambda, hpd_lambda, by = 0) + colnames(concepts_lambda)[colnames(concepts_lambda) == "colMeans(concepts_lambda)"] <- "mean" + colnames(concepts_lambda)[colnames(concepts_lambda) == "lower"] <- "HPD2.5" + colnames(concepts_lambda)[colnames(concepts_lambda) == "upper"] <- "HPD97.5" + + concepts_difficulty <- concepts_lambda[grepl("\\.1$", concepts_lambda$Row.names), ] # lambda.1 = difficulty because multiplied by one (see first equation on MCMCordfactanal help page) + concepts_difficulty$Row.names <- gsub("^Lambda|\\.1$", "", concepts_difficulty$Row.names) + at_difficulty <- at[at$value %in% concepts_difficulty$Row.names, ] + concepts_difficulty <- merge(concepts_difficulty, at_difficulty, by.x = "Row.names", by.y = "value") + concepts_difficulty <- concepts_difficulty[, !(colnames(concepts_difficulty) == "ID")] + dna_scale$difficulty <- concepts_difficulty + + concepts_discrimination <- concepts_lambda[grepl("\\.2$", concepts_lambda$Row.names), ] # lambda.2 = discrimination because multiplied by phi_i + concepts_discrimination$Row.names <- gsub("^Lambda|\\.2$", "", concepts_discrimination$Row.names) + at_discrimination <- at[at$value %in% concepts_discrimination$Row.names, ] + concepts_discrimination <- merge(concepts_discrimination, at_discrimination, by.x = "Row.names", by.y = "value") + concepts_discrimination <- concepts_discrimination[, !(colnames(concepts_discrimination) == "ID")] + dna_scale$discrimination <- concepts_discrimination + } + dna_scale$call <- mget(names(formals()), sys.frame(sys.nframe())) + dna_scale$call$connection <- NULL + class(dna_scale) <- c("dna_scale1dord", class(dna_scale)) + class(dna_scale) <- c("dna_scale", class(dna_scale)) + return(dna_scale) +} + +#' @rdname dna_scaling +#' @export +dna_scale2dbin <- function(statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + item_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + alpha_beta_start = NA, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ...) { + + if (!requireNamespace("MCMCpack", quietly = TRUE)) { + stop("The DNA scaling functions require the 'MCMCpack' package to be installed.\n", + "To do this, enter 'install.packages(\"MCMCpack\")'.") + } + if (!requireNamespace("coda", quietly = TRUE)) { + stop("The DNA scaling functions require the 'coda' package to be installed.\n", + "To do this, enter 'install.packages(\"coda\")'.") + } + + dots <- list(...) + out <- bin_recode(statementType = statementType, + dots = dots, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + zero_as_na = zero_as_na, + threshold = threshold, + drop_min_actors = drop_min_actors, + drop_min_concepts = drop_min_concepts, + store_variables = store_variables) + nw2 <- out$nw2 + dots <- out$dots + dots_nw <- out$dots_nw + invertValues <- out$invertValues + excludeValues <- out$excludeValues + # Scaling + x <- do.call(eval(parse(text = "MCMCpack::MCMCirtKd")), c(list( + nw2, + dimensions = 2, + item.constraints = item_constraints, + burnin = mcmc_burnin, + mcmc = mcmc_iterations, + thin = mcmc_thin, + verbose = ifelse(verbose == TRUE, + ((mcmc_iterations + mcmc_burnin) / 10), + verbose), + seed = seed, + alphabeta.start = alpha_beta_start, + b0 = alpha_beta_prior_mean, + B0 = alpha_beta_prior_variance, + store.item = (store_variables == variable2 | store_variables == "both"), + store.ability = (store_variables == variable1 | store_variables == "both"), + drop.constant.items = drop_constant_concepts), + dots)) + dna_scale <- list() + dna_scale$sample <- x + # Store actor frequency for possible min argument in dna_plotScale + nw_freq <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (store_variables == variable1 | store_variables == "both") { + actors <- x[, grepl("^theta.", colnames(x))] + hpd <- as.data.frame(coda::HPDinterval(actors, prob = 0.95)) + actors <- as.data.frame(colMeans(actors)) + actors <- merge(actors, hpd, by = 0) + colnames(actors)[colnames(actors) == "colMeans(actors)"] <- "mean" + colnames(actors)[colnames(actors) == "lower"] <- "HPD2.5" + colnames(actors)[colnames(actors) == "upper"] <- "HPD97.5" + actors1 <- actors[grepl(".1$", actors$Row.names), drop = FALSE, ] + actors1$Row.names <- gsub("^theta.|.1$", "", actors1$Row.names) + actors2 <- actors[grepl(".2$", actors$Row.names), drop = FALSE, ] + actors2$Row.names <- gsub("^theta.|.2$", "", actors2$Row.names) + actors <- merge(actors1, + actors2, + by = "Row.names", + suffixes = c("_dim1", "_dim2")) + at <- dna_getAttributes(statementType = statementType, variable = variable1) + at <- at[at$value %in% actors$Row.names, ] + at$frequency <- rowSums(nw_freq)[match(at$value, rownames(nw_freq))] + actors <- merge(actors, at, by.x = "Row.names", by.y = "value") + actors <- actors[, !(colnames(actors) == "ID")] + dna_scale$ability <- actors + } + if (store_variables == variable2 | store_variables == "both") { + at_alpha <- at_beta <- dna_getAttributes(statementType = statementType, variable = variable2) + concepts_beta <- x[, grepl("^beta.", colnames(x))] # beta = discrimination parameters + hpd_beta <- as.data.frame(coda::HPDinterval(concepts_beta, prob = 0.95)) + concepts_beta <- as.data.frame(colMeans(concepts_beta)) + concepts_beta <- merge(concepts_beta, hpd_beta, by = 0) + colnames(concepts_beta)[colnames(concepts_beta) == "colMeans(concepts_beta)"] <- "mean" + colnames(concepts_beta)[colnames(concepts_beta) == "lower"] <- "HPD2.5" + colnames(concepts_beta)[colnames(concepts_beta) == "upper"] <- "HPD97.5" + concepts_beta1 <- concepts_beta[grepl("\\.1$", concepts_beta$Row.names), drop = FALSE, ] + concepts_beta1$Row.names <- gsub("^beta\\.|\\.1$", "", concepts_beta1$Row.names) + concepts_beta2 <- concepts_beta[grepl("\\.2$", concepts_beta$Row.names), drop = FALSE, ] + concepts_beta2$Row.names <- gsub("^beta\\.|\\.2$", "", concepts_beta2$Row.names) + concepts_beta <- merge(concepts_beta1, concepts_beta2, by = "Row.names", suffixes = c("_dim1", "_dim2")) + at_beta <- at_beta[at_beta$value %in% concepts_beta$Row.names, ] + concepts_beta <- merge(concepts_beta, at_beta, by.x = "Row.names", by.y = "value") + concepts_beta <- concepts_beta[, !(colnames(concepts_beta) == "ID")] + dna_scale$discrimination <- concepts_beta + + concepts_alpha <- x[, grepl("^alpha.", colnames(x))] # alpha = difficulty parameters + hpd_alpha <- as.data.frame(coda::HPDinterval(concepts_alpha, prob = 0.95)) + concepts_alpha <- as.data.frame(colMeans(concepts_alpha)) + concepts_alpha <- merge(concepts_alpha, hpd_alpha, by = 0) + colnames(concepts_alpha)[colnames(concepts_alpha) == "colMeans(concepts_alpha)"] <- "mean" + colnames(concepts_alpha)[colnames(concepts_alpha) == "lower"] <- "HPD2.5" + colnames(concepts_alpha)[colnames(concepts_alpha) == "upper"] <- "HPD97.5" + concepts_alpha$Row.names <- gsub("^alpha\\.", "", concepts_alpha$Row.names) + at_alpha <- at_alpha[at_alpha$value %in% concepts_alpha$Row.names, ] + concepts_alpha <- merge(concepts_alpha, at_alpha, by.x = "Row.names", by.y = "value") + concepts_alpha <- concepts_alpha[, !(colnames(concepts_alpha) == "ID")] + dna_scale$difficulty <- concepts_alpha + } + dna_scale$call <- mget(names(formals()), sys.frame(sys.nframe())) + dna_scale$call$connection <- NULL + class(dna_scale) <- c("dna_scale2dbin", class(dna_scale)) + class(dna_scale) <- c("dna_scale", class(dna_scale)) + return(dna_scale) +} + +#' @rdname dna_scaling +#' @export +dna_scale2dord <- function(statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + lambda_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_tune = 1.5, + lambda_start = NA, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ...) { + + if (!requireNamespace("MCMCpack", quietly = TRUE)) { + stop("The DNA scaling functions require the 'MCMCpack' package to be installed.\n", + "To do this, enter 'install.packages(\"MCMCpack\")'.") + } + if (!requireNamespace("coda", quietly = TRUE)) { + stop("The DNA scaling functions require the 'coda' package to be installed.\n", + "To do this, enter 'install.packages(\"coda\")'.") + } + + dots <- list(...) + out <- ord_recode(statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + zero_as_na = zero_as_na, + threshold = threshold, + drop_min_actors = drop_min_actors, + drop_min_concepts = drop_min_concepts, + store_variables = store_variables, + dots = dots) + nw2 <- out$nw2 + dots <- out$dots + dots_nw <- out$dots_nw + invertValues <- out$invertValues + excludeValues <- out$excludeValues + # Scaling + x <- do.call(eval(parse(text = "MCMCpack::MCMCordfactanal")), c(list( + nw2, + factors = 2, + lambda.constraints = lambda_constraints, + burnin = mcmc_burnin, + mcmc = mcmc_iterations, + thin = mcmc_thin, + tune = mcmc_tune, + verbose = ifelse(verbose == TRUE, + ((mcmc_iterations + mcmc_burnin) / 10), + verbose), + seed = seed, + lambda.start = lambda_start, + l0 = lambda_prior_mean, + L0 = lambda_prior_variance, + store.lambda = (store_variables == variable2 | store_variables == "both"), + store.scores = (store_variables == variable1 | store_variables == "both"), + drop.constantvars = drop_constant_concepts), + dots)) + dna_scale <- list() + dna_scale$sample <- x + # Store actor frequency for possible min argument in dna_plotScale + nw_freq <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (store_variables == variable1 | store_variables == "both") { + actors <- x[, grepl("^phi.", colnames(x))] + hpd <- as.data.frame(coda::HPDinterval(actors, prob = 0.95)) + actors <- as.data.frame(colMeans(actors)) + actors <- merge(actors, hpd, by = 0) + colnames(actors)[colnames(actors) == "colMeans(actors)"] <- "mean" + colnames(actors)[colnames(actors) == "lower"] <- "HPD2.5" + colnames(actors)[colnames(actors) == "upper"] <- "HPD97.5" + actors1 <- actors[grepl(".2$", actors$Row.names), drop = FALSE, ] + actors1$Row.names <- gsub("^phi.|.2$", "", actors1$Row.names) + actors2 <- actors[grepl(".3$", actors$Row.names), drop = FALSE, ] + actors2$Row.names <- gsub("^phi.|.3$", "", actors1$Row.names) + actors <- merge(actors1, + actors2, + by = "Row.names", + suffixes = c("_dim1", "_dim2")) + at <- dna_getAttributes(statementType = statementType, + variable = variable1) + at <- at[at$value %in% actors$Row.names, ] + at$frequency <- rowSums(nw_freq)[match(at$value, rownames(nw_freq))] + actors <- merge(actors, at, by.x = "Row.names", by.y = "value") + actors <- actors[, !(colnames(actors) == "ID")] + dna_scale$ability <- actors + } + if (store_variables == variable2 | store_variables == "both") { + at_lambda1 <- at_lambda23 <- dna_getAttributes(statementType = statementType, variable = variable2) + concepts_lambda23 <- x[, grepl("^Lambda.+\\.(2|3)$", colnames(x))] # Lambda 2 = discrimination parameters, first dimension; Lambda 3 = second dimension + hpd_lambda23 <- as.data.frame(coda::HPDinterval(concepts_lambda23, prob = 0.95)) + concepts_lambda23 <- as.data.frame(colMeans(concepts_lambda23)) + concepts_lambda23 <- merge(concepts_lambda23, hpd_lambda23, by = 0) + colnames(concepts_lambda23)[colnames(concepts_lambda23) == "colMeans(concepts_lambda23)"] <- "mean" + colnames(concepts_lambda23)[colnames(concepts_lambda23) == "lower"] <- "HPD2.5" + colnames(concepts_lambda23)[colnames(concepts_lambda23) == "upper"] <- "HPD97.5" + concepts_lambda2 <- concepts_lambda23[grepl("\\.2$", concepts_lambda23$Row.names), drop = FALSE, ] + concepts_lambda2$Row.names <- gsub("^Lambda|\\.2$", "", concepts_lambda2$Row.names) + concepts_lambda3 <- concepts_lambda23[grepl("\\.3$", concepts_lambda23$Row.names), drop = FALSE, ] + concepts_lambda3$Row.names <- gsub("^Lambda|\\.3$", "", concepts_lambda3$Row.names) + concepts_lambda23 <- merge(concepts_lambda2, concepts_lambda3, by = "Row.names", suffixes = c("_dim1", "_dim2"), all = TRUE) + at_lambda23 <- at_lambda23[at_lambda23$value %in% concepts_lambda23$Row.names, ] + concepts_lambda23 <- merge(concepts_lambda23, at_lambda23, by.x = "Row.names", by.y = "value") + concepts_lambda23 <- concepts_lambda23[, !(colnames(concepts_lambda23) == "ID")] + dna_scale$discrimination <- concepts_lambda23 + + concepts_lambda1 <- x[, grepl("^Lambda.+\\.1$", colnames(x))] # Lambda 1 = difficulty parameters + hpd_lambda1 <- as.data.frame(coda::HPDinterval(concepts_lambda1, prob = 0.95)) + concepts_lambda1 <- as.data.frame(colMeans(concepts_lambda1)) + concepts_lambda1 <- merge(concepts_lambda1, hpd_lambda1, by = 0) + colnames(concepts_lambda1)[colnames(concepts_lambda1) == "colMeans(concepts_lambda1)"] <- "mean" + colnames(concepts_lambda1)[colnames(concepts_lambda1) == "lower"] <- "HPD2.5" + colnames(concepts_lambda1)[colnames(concepts_lambda1) == "upper"] <- "HPD97.5" + concepts_lambda1$Row.names <- gsub("^Lambda|\\.1$", "", concepts_lambda1$Row.names) + at_lambda1 <- at_lambda1[at_lambda1$value %in% concepts_lambda1$Row.names, ] + concepts_lambda1 <- merge(concepts_lambda1, at_lambda1, by.x = "Row.names", by.y = "value") + concepts_lambda1 <- concepts_lambda1[, !(colnames(concepts_lambda1) == "ID")] + dna_scale$difficulty <- concepts_lambda1 + } + dna_scale$call <- mget(names(formals()), sys.frame(sys.nframe())) + dna_scale$call$connection <- NULL + class(dna_scale) <- c("dna_scale2dord", class(dna_scale)) + class(dna_scale) <- c("dna_scale", class(dna_scale)) + return(dna_scale) +} + +#' @noRd +bin_recode <- function(statementType, + variable1, + variable2, + qualifier, + zero_as_na, + threshold, + drop_min_actors, + drop_min_concepts, + store_variables, + dots) { + if ("excludeValues" %in% names(dots)) { + excludeValues <- dots["excludeValues"][[1]] + dots["excludeValues"] <- NULL + } else { + excludeValues <- list() + } + if ("invertValues" %in% names(dots)) { + invertValues <- dots["invertValues"][[1]] + dots["invertValues"] <- NULL + } else { + invertValues <- FALSE + } + if ("normalization" %in% names(dots)) { + dots["normalization"] <- NULL + warning("'normalization' is not supported in dna_scale and will be ", + "ignored.") + } + if ("qualifierAggregation" %in% names(dots)) { + dots["qualifierAggregation"] <- NULL + warning("'qualifierAggregation' is not supported in dna_scale and ", + "will be ignored.") + } + if (any(names(formals(dna_network)) %in% names(dots))) { + dots_nw <- dots[names(dots) %in% names(formals(dna_network))] + dots[names(dots) %in% names(formals(dna_network))] <- NULL + } else { + dots_nw <- list() + } + if (!is.character(variable1) | !is.character(variable2)) { + stop ("'variable1' and 'variable2' must be provided as character objects.") + } + if (!is.character(store_variables)) { + stop ("'store_variables' must be provided as a character object.") + } + if (isTRUE(threshold > 1)) { + threshold <- threshold / 100 + } + if (!(store_variables == "both" | + store_variables == variable1 | + store_variables == variable2)) { + stop ("'store_variables' does not match with 'variable1' or 'variable2'. ", + "Please match 'store_variables' with variables in 'variable1' or ", + "'variable2', or use \"both\" in case you want to store both ", + "variables.") + } + # Check if non-binary structure in agreement + nw <- do.call("dna_network", c(list(networkType = "eventlist", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if ("character" %in% class(nw[, qualifier]) || !all(unique(nw[, qualifier])) %in% c(0, 1)) { + nw <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + nw2 <- as.matrix(nw) + nw2[nw2 > 1] <- 1 + if (isTRUE(zero_as_na)) { + zero_as_na <- FALSE + warning("Setting 'zero_as_na' to FALSE because there are otherwise only 1s in the data matrix.") + } + if (!is.null(threshold)) { + warning("'threshold' is not supported and will be ignored.") + } + } else { + # retrieve data from network + nw <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "combine", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (is.null(threshold)) { + # change structure of network according to scaling type + nw2 <- nw + if (isTRUE(zero_as_na)) { + nw2[nw == 0 | nw == 3] <- NA + } else { + nw2[nw == 3] <- 0 + } + nw2[nw == 2] <- 0 + } else { + # Include threshold in export of network + nw_pos <- do.call("dna_network", c(list( + networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + isolates = TRUE, + excludeValues = c(list("agreement" = + ifelse(invertValues, 1, 0)), + excludeValues), + invertValues = invertValues), + dots_nw)) + nw_neg <- do.call("dna_network", c(list( + networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + isolates = TRUE, + excludeValues = c(list("agreement" = + ifelse(invertValues, 0, 1)), + excludeValues), + invertValues = invertValues), + dots_nw)) + nw_com <- nw_pos / (nw_pos + nw_neg) + nw2 <- nw_com + if (threshold > 0.5) { + threshold <- 1 - threshold + } + nw2[is.nan(nw_com)] <- NA + nw2[nw_com > threshold & nw_com < 1 - threshold] <- NA + if (isFALSE(zero_as_na)) { + nw2[is.na(nw2)] <- 0 + } + nw2[nw_com <= threshold] <- 0 + nw2[nw_com >= 1 - threshold] <- 1 + nw2 <- nw2[match(rownames(nw), rownames(nw2)), + match(colnames(nw), colnames(nw2))] + } + } + if (isTRUE(drop_min_actors > 1) | isTRUE(drop_min_concepts > 1)) { + nw_exclude <- nw2 + nw_exclude[nw_exclude == 0] <- 1 + nw_exclude[is.na(nw_exclude)] <- 0 + if (isTRUE(drop_min_actors > 1)) { + if (drop_min_actors > max(rowSums(nw_exclude))) { + stop(paste0("The specified number in 'drop_min_actors' is higher than ", + "the maximum number of concepts mentioned by an actor (", + max(rowSums(nw_exclude))), ").") + } + nw2 <- nw2[rowSums(nw_exclude) >= drop_min_actors, ] + } + if (isTRUE(drop_min_concepts > 1)) { + if (drop_min_concepts > max(colSums(nw_exclude))) { + stop(paste0("The specified number in 'drop_min_concepts' is higher ", + "than the maximum number of jointly mentioned concepts (", + max(colSums(nw_exclude))), ").") + } + nw2 <- nw2[, colSums(nw_exclude) >= drop_min_concepts] + } + } + # Test if actor is without any statements + filter_actor <- sapply(rownames(nw2), function(c) { + !sum(is.na(nw2[c, ]) * 1) >= ncol(nw2) + }) + # Test if only one concept used by actor + filter_concept <- sapply(colnames(nw2), function(c) { + !sum(is.na(nw2[, c]) * 1) >= nrow(nw2) - 1 + }) + nw2 <- nw2[filter_actor, filter_concept] + if ("FALSE" %in% filter_concept) { + if (drop_min_actors > 1 & drop_min_concepts >= 2) { + warning("After deleting actors with 'drop_min_actors', some concepts ", + "are now mentioned by less than the two required actors. The ", + "follwing concepts have therefore not been included in the ", + "scaling:\n", + paste(names(filter_concept[filter_concept == FALSE]), + collapse = "\n")) + } else { + warning("dna_scale requires concepts mentioned by at least two actors. ", + "The following concepts have therefore not been included in the ", + "scaling:\n", + paste(names(filter_concept[filter_concept == FALSE]), + collapse = "\n")) + } + } + if ("FALSE" %in% filter_actor) { + if (drop_min_concepts >= 1) { + warning("After deleting concepts with 'drop_min_concepts', some actors ", + "now have less than one statement. The following actors have ", + "therefore not been included in the scaling:\n", + paste(names(filter_actor[filter_actor == FALSE]), + collapse = "\n")) + } else { + warning("Some actors do not have any statements and were not included in", + " the scaling. Setting or lowering the 'threshold' might include", + " them:\n", + paste(names(filter_actor[filter_actor == FALSE]), + collapse = "\n")) + } + } + out <- list(nw2 = nw2, + dots = dots, + dots_nw = dots_nw, + excludeValues = excludeValues, + invertValues = invertValues) + return(out) +} + +#' @noRd +ord_recode <- function(statementType, + variable1, + variable2, + qualifier, + zero_as_na, + threshold, + drop_min_actors, + drop_min_concepts, + store_variables, + dots) { + if ("excludeValues" %in% names(dots)) { + excludeValues <- dots["excludeValues"][[1]] + dots["excludeValues"] <- NULL + } else { + excludeValues <- list() + } + if ("invertValues" %in% names(dots)) { + invertValues <- dots["invertValues"][[1]] + dots["invertValues"] <- NULL + } else { + invertValues <- FALSE + } + if ("normalization" %in% names(dots)) { + dots["normalization"] <- NULL + warning("'normalization' is not supported in dna_scale and will be ", + "ignored.") + } + if ("qualifierAggregation" %in% names(dots)) { + dots["qualifierAggregation"] <- NULL + warning("'qualifierAggregation' is not supported in dna_scale and will be ", + "ignored.") + } + if (any(names(formals(dna_network)) %in% names(dots))) { + dots_nw <- dots[names(dots) %in% names(formals(dna_network))] + dots[names(dots) %in% names(formals(dna_network))] <- NULL + } else { + dots_nw <- list() + } + if (!is.character(variable1) | !is.character(variable2)) { + stop ("'variable1' and 'variable2' must be provided as character objects.") + } + if (!is.character(store_variables)) { + stop ("'store_variables' must be provided as a character object.") + } + if (isTRUE(threshold > 1)) { + threshold <- threshold / 100 + } + if (!(store_variables == "both" | + store_variables == variable1 | + store_variables == variable2)) { + stop ("'store_variables' does not match with 'variable1' or 'variable2'. ", + "Please match 'store_variables' with variables in 'variable1' or ", + "'variable2', or use \"both\" in case you want to store both ", + "variables.") + } + # Check if non-binary structure in agreement + nw <- do.call("dna_network", c(list(networkType = "eventlist", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if ("character" %in% class(nw[, qualifier]) || !!all(unique(nw[, qualifier])) %in% c(0, 1)) { + nw <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + nw2 <- nw + nw2[nw > 1] <- 1 + if (zero_as_na == TRUE) { + zero_as_na <- FALSE + warning("Setting 'zero_as_na' to FALSE because there are otherwise only 1s in the data matrix.") + } + if (!is.null(threshold)) { + threshold <- NULL + warning("'threshold' is not supported and will be ignored.") + } + } else { + # retrieve data from network + nw <- do.call("dna_network", c(list(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "combine", + excludeValues = excludeValues, + invertValues = invertValues), + dots_nw)) + if (is.null(threshold)) { + # change structure of network according to scaling type + nw2 <- nw + if (zero_as_na == TRUE) { + nw2[nw == 0] <- NA + nw2[nw == 1] <- 3 + nw2[nw == 2] <- 1 + nw2[nw == 3] <- 2 + } else if (zero_as_na == FALSE) { + nw2[nw == 0] <- 2 + nw2[nw == 1] <- 3 + nw2[nw == 2] <- 1 + nw2[nw == 3] <- 2 + } + } else { + # Include threshold in export of network + nw_pos <- do.call("dna_network", c(list( + networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + isolates = TRUE, + excludeValues = c(list("agreement" = + ifelse(invertValues, 1, 0)), + excludeValues), + invertValues = invertValues), + dots_nw)) + nw_neg <- do.call("dna_network", c(list( + networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable2 = variable2, + qualifier = qualifier, + qualifierAggregation = "ignore", + isolates = TRUE, + excludeValues = c(list("agreement" = + ifelse(invertValues, 0, 1)), + excludeValues), + invertValues = invertValues), + dots_nw)) + nw_com <- nw_pos / (nw_pos + nw_neg) + nw2 <- nw_com + if (threshold > 0.5) { + threshold <- 1 - threshold + } + nw2[is.nan(nw_com)] <- NA + nw2[nw_com < threshold & nw_com > 1 - threshold] <- 2 + nw2[nw_com <= -threshold] <- 1 + nw2[nw_com >= threshold] <- 3 + if (isFALSE(zero_as_na)) { + nw2[is.na(nw2)] <- 2 + } + nw2 <- nw2[match(rownames(nw), rownames(nw2)), + match(colnames(nw), colnames(nw2))] + } + } + if (isTRUE(drop_min_actors > 1) | isTRUE(drop_min_concepts > 1)) { + if (zero_as_na == FALSE) { + if (is.null(threshold)) { + nw_exclude <- nw + nw_exclude[nw_exclude > 1] <- 1 + } else { + nw_exclude <- nw_com + nw_exclude[!is.nan(nw_exclude)] <- 1 + nw_exclude[is.nan(nw_exclude)] <- 0 + } + } else { + nw_exclude <- nw2 + nw_exclude[nw_exclude > 1] <- 1 + nw_exclude[is.na(nw_exclude)] <- 0 + } + if (isTRUE(drop_min_actors > 1)) { + if (drop_min_actors > max(rowSums(nw_exclude))) { + stop(paste0("The specified number in 'drop_min_actors' is higher than ", + "the maximum number of concepts mentioned by an actor (", + max(rowSums(nw_exclude))), ").") + } + nw2 <- nw2[rowSums(nw_exclude) >= drop_min_actors, ] + } + if (isTRUE(drop_min_concepts > 1)) { + if (drop_min_concepts > max(colSums(nw_exclude))) { + stop(paste0("The specified number in 'drop_min_concepts' is higher ", + "than the maximum number of jointly mentioned concepts (", + max(colSums(nw_exclude))), ").") + } + nw2 <- nw2[, colSums(nw_exclude) >= drop_min_concepts] + } + } + # Test if actor is without any statements + filter_actor <- sapply(rownames(nw2), function(c) { + !sum(is.na(nw2[c, ]) * 1) >= ncol(nw2) + }) + # Test if only one concept used by actor + filter_concept <- sapply(colnames(nw2), function(c) { + !sum(is.na(nw2[, c]) * 1) >= nrow(nw2) - 1 + }) + nw2 <- nw2[filter_actor, filter_concept] + if ("FALSE" %in% filter_concept) { + if (drop_min_actors > 1 & drop_min_concepts >= 2) { + warning("After deleting actors with 'drop_min_actors', some concepts ", + "are now mentioned by less than the two required actors. The ", + "follwing concepts have therefore not been included in the ", + "scaling:\n", + paste(names(filter_concept[filter_concept == FALSE]), + collapse = "\n")) + } else { + warning("dna_scale requires concepts mentioned by at least two actors. ", + "The following concepts have therefore not been included in the ", + "scaling:\n", + paste(names(filter_concept[filter_concept == FALSE]), + collapse = "\n")) + } + } + if ("FALSE" %in% filter_actor) { + if (drop_min_concepts >= 1) { + warning("After deleting concepts with 'drop_min_concepts', some actors ", + "now have less than one statement. The following actors have ", + "therefore not been included in the scaling:\n", + paste(names(filter_actor[filter_actor == FALSE]), + collapse = "\n")) + } else { + warning("Some actors do not have any statements and were not included in", + " the scaling. Setting or lowering the 'threshold' might include", + " them:\n", + paste(names(filter_actor[filter_actor == FALSE]), + collapse = "\n")) + } + } + out <- list(nw2 = nw2, + dots = dots, + dots_nw = dots_nw, + excludeValues = excludeValues, + invertValues = invertValues) + return(out) +} + +#' @param x A \code{dna_scale} object. +#' @rdname dna_scaling +#' @export +print.dna_scale <- function(x, trim = 60, ...) { + cat("Method:", class(x)[2], "\n") + if (grepl("scale1d", class(x)[2])) { + if ("ability" %in% names(x)) { + cat(paste0("\nAbility parameters (variable '", x$call$variable1, "'):\n")) + ability <- x$ability[, c("mean", "HPD2.5", "HPD97.5")] + rownames(ability) <- sapply(x$ability$Row.names, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + ability <- ability[order(ability$mean, decreasing = TRUE), ] + print(ability) + } + if ("discrimination" %in% names(x)) { + cat(paste0("\nItem discrimination parameters (variable '", x$call$variable2, "'):\n")) + discrimination <- x$discrimination[, c("mean", "HPD2.5", "HPD97.5")] + rownames(discrimination) <- sapply(x$discrimination$Row.names, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + discrimination <- discrimination[order(discrimination$mean, decreasing = TRUE), ] + print(discrimination) + } + } else { + if ("ability" %in% names(x)) { + cat(paste0("\nAbility parameters (variable '", x$call$variable1, "'):\n")) + ability <- x$ability[, c("mean_dim1", "HPD2.5_dim1", "HPD97.5_dim1", "mean_dim2", "HPD2.5_dim2", "HPD97.5_dim2")] + rownames(ability) <- sapply(x$ability$Row.names, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + ability <- ability[order(ability$mean_dim1, decreasing = TRUE), ] + print(ability) + } + if ("discrimination" %in% names(x)) { + cat(paste0("\nItem discrimination parameters (variable '", x$call$variable2, "'):\n")) + discrimination <- x$discrimination[, c("mean_dim1", "HPD2.5_dim1", "HPD97.5_dim1", "mean_dim2", "HPD2.5_dim2", "HPD97.5_dim2")] + rownames(discrimination) <- sapply(x$discrimination$Row.names, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + discrimination <- discrimination[order(discrimination$mean_dim1, decreasing = TRUE), ] + print(discrimination) + } + } + if ("difficulty" %in% names(x)) { + cat(paste0("\nItem difficulty parameters (variable '", x$call$variable2, "'):\n")) + difficulty <- x$difficulty[, c("mean", "HPD2.5", "HPD97.5")] + rownames(difficulty) <- sapply(x$difficulty$Row.names, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + difficulty <- difficulty[order(difficulty$mean, decreasing = TRUE), ] + print(difficulty) + } +} + +#' @param object A \code{dna_scale} object, created by one of the scaling functions. +#' @param type The type(s) of plot to generate. Must be one or more of the following: +#' \itemize{ +#' \item \code{"trace"}: For creating MCMC trace plots. +#' \item \code{"density"}: For creating MCMC density plots. +#' \item \code{"scaling"}: For creating plots summarizing the estimated parameters. +#' } +#' @param parameters The parameter type(s) to plot. Must be one or more of the following: +#' \itemize{ +#' \item \code{"ability"}: The ability parameters for the actors, indicating their ideology. +#' \item \code{"discrimination"}: The item discrimination parameters. +#' \item \code{"difficulty"}: The item difficulty parameters. +#' } +#' @param trim An integer defining the maximum length of the labels. +#' @param nrow An integer defining how many rows of parameters should be in a trace or density plot. +#' @param ncol An integer defining how many columns of parameters should be in a trace or density plot. +#' @rdname dna_scaling +#' @importFrom stats reorder reshape +#' @export +autoplot.dna_scale <- function(object, + ..., + type = c("trace", "density", "scaling"), + parameters = c("ability", "discrimination", "difficulty"), + trim = 40, + nrow = 5, + ncol = 3) { + # ensure type and parameters are matched correctly + type <- match.arg(type, several.ok = TRUE) + parameters <- match.arg(parameters, c("ability", "discrimination", "difficulty"), several.ok = TRUE) + + # check for required packages + if (any(grepl("^dna_scale2d", class(object))) && "scaling" %in% type && !requireNamespace("ggrepel", quietly = TRUE)) { + stop("The 'ggrepel' package is required for plotting 2D scalings.\n", + "To install it, enter 'install.packages(\"ggrepel\")'.") + } + + # helper function to trim labels + trim_label <- function(label, trim) { + if (nchar(label) > trim) { + if (grepl(".+\\.(1|2|3)$", label)) { + return(paste0(substr(label, 1, trim - 3), "*", substr(label, nchar(label) - 1, nchar(label)))) + } else { + return(paste0(substr(label, 1, trim - 1), "*")) + } + } else { + return(label) + } + } + + # helper function to create trace or density plots + create_diagnostic_plots <- function(samples, param_type, plot_type) { + tp <- t(samples) + tpdf <- as.data.frame(tp) # transposed parameters data frame := tpdf + tpdf$Row.names <- tpdf$parameter <- rownames(tpdf) + tpdf$Row.names <- gsub("((^theta\\.)|(^alpha\\.)|(^beta\\.)|(^phi\\.)|(^Lambda))|(\\.(1|2|3)$)", "", tpdf$Row.names) + if (param_type == "ability") { + at <- object$ability[, c("Row.names", "color")] + tpdf <- tpdf[grepl("(^phi\\.)|(^theta\\.)", tpdf$parameter), ] + } else { + at <- object$discrimination[, c("Row.names", "color")] + if (param_type == "discrimination") { + tpdf <- tpdf[grepl("(^beta\\.)|(^Lambda.+\\.(2|3)$)", tpdf$parameter), ] + } else if (param_type == "difficulty") { + tpdf <- tpdf[grepl("(^alpha\\.)|(^Lambda.+\\.1$)", tpdf$parameter), ] + } + } + tpdf <- merge(tpdf, at, all.x = TRUE, all.y = FALSE) + if (any(grepl("1dord$", class(object))) || (any(grepl("2dord$", class(object))) && param_type == "difficulty")) { + tpdf$parameter <- gsub("\\.(1|2)$", "", tpdf$parameter) + } else if (any(grepl("2dord$", class(object))) && param_type %in% c("ability", "discrimination")) { + tpdf$parameter <- gsub("\\.1$", ".0", tpdf$parameter) + tpdf$parameter <- gsub("\\.2$", ".1", tpdf$parameter) + tpdf$parameter <- gsub("\\.3$", ".2", tpdf$parameter) + } + tpdf$parameter <- gsub("(^theta\\.)|(^alpha\\.)|(^beta\\.)|(^phi\\.)|(^Lambda)", "", tpdf$parameter) + tpdf$parameter <- sapply(tpdf$parameter, trim_label, trim = trim) + tpdf_long <- stats::reshape(tpdf, + varying = list(colnames(tpdf)[2:(ncol(tpdf) - 2)]), + v.names = "value", + timevar = "variable", + times = colnames(tpdf)[2:(ncol(tpdf) - 2)], + direction = "long") + rownames(tpdf_long) <- NULL + tpdf_long$Iteration <- sort(rep(1:nrow(samples), nrow(tpdf))) + tpdf_long <- tpdf_long[order(tpdf_long$parameter, tpdf_long$Iteration), -which(colnames(tpdf_long) %in% c("Row.names", "variable", "id"))] + + plots_needed <- ceiling(nrow(tpdf) / (nrow * ncol)) # maximum number of panels per plot in denominator + plots <- list() + if (plot_type == "trace") { + for (i in 1:plots_needed) { + subset_df <- tpdf_long[tpdf_long$parameter %in% unique(tpdf_long$parameter)[((i - 1) * (nrow * ncol) + 1):(i * (nrow * ncol))], ] + trace_plot <- ggplot2::ggplot(subset_df, ggplot2::aes(x = .data[["Iteration"]], y = .data[["value"]], color = .data[["color"]])) + + ggplot2::geom_line() + + ggplot2::facet_wrap(~parameter, scales = "free_y", ncol = ncol, nrow = nrow) + + ggplot2::scale_color_identity() + + ggplot2::labs(title = paste("Trace plots for", param_type, "parameters")) + + ggplot2::theme_minimal() + + ggplot2::theme(legend.position = "none", + axis.title = ggplot2::element_blank()) + plots <- c(plots, list(trace_plot)) + } + } else if (plot_type == "density") { + for (i in 1:plots_needed) { + subset_df <- tpdf_long[tpdf_long$parameter %in% unique(tpdf_long$parameter)[((i - 1) * (nrow * ncol) + 1):(i * (nrow * ncol))], ] + density_plot <- ggplot2::ggplot(subset_df, ggplot2::aes(x = .data[["value"]], fill = .data[["color"]])) + + ggplot2::geom_density(alpha = 0.5) + + ggplot2::facet_wrap(~parameter, scales = "free", ncol = ncol, nrow = nrow) + + ggplot2::scale_fill_identity() + + ggplot2::labs(title = paste("Density plots for", param_type, "parameters")) + + ggplot2::theme_minimal() + + ggplot2::theme(legend.position = "none", + axis.title = ggplot2::element_blank()) + plots <- c(plots, list(density_plot)) + } + } + return(plots) + } + + # helper function to create scaling plot + create_scaling_plot_1d <- function(df, param_name) { + df$Row.names <- sapply(df$Row.names, trim_label, trim = trim) + scaling_plot <- ggplot2::ggplot(df, ggplot2::aes(x = stats::reorder(.data[["Row.names"]], .data[["mean"]]), y = mean)) + + ggplot2::geom_hline(yintercept = 0, color = "gray", linewidth = 1.5) + + ggplot2::geom_pointrange(ggplot2::aes(ymin = .data[["HPD2.5"]], ymax = .data[["HPD97.5"]], color = .data[["color"]]), linewidth = 1) + + ggplot2::coord_flip() + + ggplot2::scale_color_identity() + + ggplot2::labs(title = paste(param_name, "parameters"), x = NULL, y = NULL) + + ggplot2::theme_minimal() + return(scaling_plot) + } + + # helper function to create 2D scaling plot + create_scaling_plot_2d <- function(df, param_name) { + df$Row.names <- sapply(df$Row.names, trim_label, trim = trim) + ggplot2::ggplot(df, ggplot2::aes(x = .data[["mean_dim1"]], y = .data[["mean_dim2"]], color = .data[["color"]])) + + ggplot2::geom_point() + + ggrepel::geom_text_repel(aes(label = .data[["Row.names"]]), max.overlaps = nrow(df)) + + ggplot2::scale_color_identity() + + ggplot2::labs(title = paste("Mean", param_name, "parameters"), x = NULL, y = NULL) + + ggplot2::theme_minimal() + } + + plots <- list() + + if ("trace" %in% type) { + if ("ability" %in% parameters && "ability" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "ability", "trace")) + } + if ("discrimination" %in% parameters && "discrimination" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "discrimination", "trace")) + } + if ("difficulty" %in% parameters && "difficulty" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "difficulty", "trace")) + } + } + + if ("density" %in% type) { + if ("ability" %in% parameters && "ability" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "ability", "density")) + } + if ("discrimination" %in% parameters && "discrimination" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "discrimination", "density")) + } + if ("difficulty" %in% parameters && "difficulty" %in% names(object)) { + plots <- c(plots, create_diagnostic_plots(object$sample, "difficulty", "density")) + } + } + + if ("scaling" %in% type) { + if ("ability" %in% parameters && "ability" %in% names(object)) { + if (any(grepl("^dna_scale2d", class(object)))) { + scaling_plot <- create_scaling_plot_2d(object$ability, "ability") + } else { + scaling_plot <- create_scaling_plot_1d(object$ability, "Ability") + } + plots <- c(plots, list(scaling_plot)) + } + if ("discrimination" %in% parameters && "discrimination" %in% names(object)) { + if (any(grepl("^dna_scale2d", class(object)))) { + scaling_plot <- create_scaling_plot_2d(object$discrimination, "discrimination") + } else { + scaling_plot <- create_scaling_plot_1d(object$discrimination, "Discrimination") + } + plots <- c(plots, list(scaling_plot)) + } + if ("difficulty" %in% parameters && "difficulty" %in% names(object)) { + scaling_plot <- create_scaling_plot_1d(object$difficulty, "Difficulty") + plots <- c(plots, list(scaling_plot)) + } + } + + return(plots) +} \ No newline at end of file diff --git a/rDNA/rDNA/man/dna_scaling.Rd b/rDNA/rDNA/man/dna_scaling.Rd new file mode 100644 index 00000000..a750d2de --- /dev/null +++ b/rDNA/rDNA/man/dna_scaling.Rd @@ -0,0 +1,631 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dna_scale.R +\name{dna_scale1dbin} +\alias{dna_scale1dbin} +\alias{dna_scale1dord} +\alias{dna_scale2dbin} +\alias{dna_scale2dord} +\alias{print.dna_scale} +\alias{autoplot.dna_scale} +\title{Ideological scaling for discourse networks using item response theory} +\usage{ +dna_scale1dbin( + statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + theta_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_normalize = FALSE, + theta_start = NA, + alpha_start = NA, + beta_start = NA, + theta_prior_mean = 0, + theta_prior_variance = 1, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.25, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ... +) + +dna_scale1dord( + statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + lambda_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_tune = 1.5, + mcmc_normalize = FALSE, + lambda_start = NA, + lambda_prior_mean = 0, + lambda_prior_variance = 1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ... +) + +dna_scale2dbin( + statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + item_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + alpha_beta_start = NA, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ... +) + +dna_scale2dord( + statementType = "DNA Statement", + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = NULL, + lambda_constraints = NULL, + mcmc_iterations = 20000, + mcmc_burnin = 1000, + mcmc_thin = 10, + mcmc_tune = 1.5, + lambda_start = NA, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + drop_min_concepts = 2, + verbose = TRUE, + seed = 12345, + ... +) + +\method{print}{dna_scale}(x, trim = 60, ...) + +\method{autoplot}{dna_scale}( + object, + ..., + type = c("trace", "density", "scaling"), + parameters = c("ability", "discrimination", "difficulty"), + trim = 40, + nrow = 5, + ncol = 3 +) +} +\arguments{ +\item{statementType}{The statement type as a character object.} + +\item{variable1}{The first variable for the scaling construction (see +\link{dna_network}). Defaults to \code{"organization"}.} + +\item{variable2}{The second variable for the scaling construction (see +\link{dna_network}). Defaults to \code{"concept"}.} + +\item{qualifier}{The qualifier variable for the scaling construction (see +\link{dna_network}). Defaults to \code{"agreement"}. If you did not use +a qualifier in the coding, you can set the qualifier to be the same value +as variable2 and estimate an ordinal model with argument +\code{zero_as_na = FALSE} (because non-mentions of concepts are interpreted +as \code{NA} in the binary model).} + +\item{zero_as_na}{Logical. Only ordinal models. If \code{TRUE}, all +non-mentions of an actor towards a concept will be recoded as \code{NA}. If +\code{FALSE} as \code{2}.} + +\item{threshold}{Numeric value that specifies when a mixed position can be +considered as agreement or disagreement. If, for example, one actor has 60 +percent of agreeing and 40 percent of disagreeing statements towards a +concept, a \code{threshold} of 0.51 will recode the actor position on this +concept as "agreement". The same accounts also for disagreeing statements. +If one actor has 60 percent of disagreeing and 40 percent of agreeing +statements, a \code{threshold} of 0.51 will recode the actor position on +this concept as "disagreement". All values in between the \code{threshold} +(e.g., 55 percent agreement and 45 percent of disagreement and a threshold +of 0.6) will be recoded as \code{NA}. If is set to \code{NULL}, all "mixed" +positions of actors will be recoded as \code{NA}. Must be strictly +positive.} + +\item{theta_constraints}{A list specifying the constraints on the actor +parameter in a one-dimensional binary model. Three forms of constraints are +possible: \code{actorname = value}, which will constrain an actor to be +equal to the specified value (e.g. \code{0}), \code{actorname = "+"}, which +will constrain the actor to be positively scaled and +\code{actorname = "-"}, which will constrain the actor to be negatively +scaled (see example).} + +\item{mcmc_iterations}{The number of iterations for the sampler (not +including the burn-in iterations, which are discarded) before thinning.} + +\item{mcmc_burnin}{The number of burn-in iterations for the sampler, which +are discarded.} + +\item{mcmc_thin}{The thinning interval for the sampler. Iterations must be +divisible by the thinning interval. The final number of samples retained in +the output equals \code{(mcmc_iterations - mcmc_burnin) / mcmc_thin}.} + +\item{mcmc_normalize}{Logical. Should the MCMC output be normalized? If +\code{TRUE}, samples are normalized to a mean of \code{0} and a standard +deviation of \code{1}.} + +\item{theta_start}{The starting values for the actor parameters in a +one-dimensional binary model. Can either be a scalar or a column vector +with as many elements as the number of actors included in the scaling. If +set to the default \code{NA}, starting values will be set according to an +eigenvalue-eigenvector decomposition of the actor agreement score.} + +\item{alpha_start}{The starting values for the concept difficulty +parameters in a one-dimensional binary model. Can either be a scalar or a +column vector with as many elements as the number of items included in the +scaling. If set to the default \code{NA}, starting values will be set +according to a series of probit regressions that condition the starting +values of the difficulty parameters.} + +\item{beta_start}{The starting values for the concept discrimination +parameters in a one-dimensional binary model. Can either be a scalar or a +column vector with as many elements as the number of items included in the +scaling. If set to the default \code{NA}, starting values will be set +according to a series of probit regressions that condition the starting +values of the discrimination parameters.} + +\item{theta_prior_mean}{A scalar value specifying the prior mean of the +actor parameters in a one-dimensional binary model.} + +\item{theta_prior_variance}{A scalar value specifying the prior inverse +variances of the actor parameters in a one-dimensional binary model.} + +\item{alpha_beta_prior_mean}{Mean of the difficulty and discrimination +parameters in a one- or two-dimensional binary model. Can either be a +scalar or a vector of length two. If a scalar, both means will be set +according to the specified value.} + +\item{alpha_beta_prior_variance}{Inverse variance of the difficulty and +discrimination parameters in a one- or two-dimensional binary model. Can +either be a scalar or a vector of length two. If a scalar, both means will +be set according to the specified value.} + +\item{store_variables}{A character vector indicating which variables should +be stored from the scaling. Can either take the value of the character +vector indicated in \code{variable1} or \code{variable2} or \code{"both"} +to store both variables. Note that saving both variables can impact the +speed of the scaling. Defaults to \code{"both"}.} + +\item{drop_constant_concepts}{Logical. Should concepts that have no +variation be deleted before the scaling? Defaults to \code{FALSE}.} + +\item{drop_min_actors}{A numeric value specifying the minimum number of +concepts actors should have mentioned to be included in the scaling. +Defaults to \code{1}.} + +\item{drop_min_concepts}{A numeric value specifying the minimum number a +concept should have been jointly mentioned by actors. Defaults to \code{2}.} + +\item{verbose}{A boolean or numeric value indicating whether the iterations +of the scaling should be printed to the R console. If set to a numeric +value, every \code{verboseth} iteration will be printed. If set to +\code{TRUE}, \code{verbose} will print the total of iterations and burn-in +divided by \code{10}.} + +\item{seed}{The random seed for the scaling.} + +\item{...}{Additional arguments passed to \link{dna_network}. Actors can, for +example, be removed with the \code{excludeValues} arguments. The scaling +can also be applied to a specific time slice by using \code{start.date} and +\code{stop.date}. For the \code{autoplot} method, this argument is not in +use.} + +\item{lambda_constraints}{A list of lists specifying constraints on the +concept parameters in an ordinal model. Note that value \code{1} in the +brackets of the argument refers to the negative item difficulty parameters, +which in general should not be constrained. Value \code{2} relates to the +item discrimination parameter and should be used for constraints on +concepts. Three forms of constraints are possible: +\code{conceptname = list(2, value)} will constrain the item discrimination +parameter to be equal to the specified value (e.g., 0). +\code{conceptname = list(2,"+")} will constrain the item discrimination +parameter to be positively scaled and \code{conceptname = list(2, "-")} +will constrain the parameter to be negatively scaled (see example).} + +\item{mcmc_tune}{Only ordinal models. The tuning parameter for the acceptance +rates of the sampler. Acceptance rates should ideally range between +\code{0.15} and \code{0.5}. Can be either a scalar or a k-vector. Must be +strictly positive.} + +\item{lambda_start}{The starting values for the concept discrimination +parameters in an ordinal model. Can be either a scalar or a matrix. If set +to \code{NA} (default), the \code{starting values} for the unconstrained +parameters in the first column are based on the observed response pattern. +The remaining unconstrained elements are set to \code{starting values} of +either \code{1.0} or \code{-1.0}, depending on the nature of the +constraint.} + +\item{lambda_prior_mean}{The prior mean of the concept discrimination +parameters in an ordinal model. Can be either a scalar or a matrix.} + +\item{lambda_prior_variance}{The prior inverse variances of the concept +discrimination parameters in an ordinal model. Can be either a scalar or a +matrix.} + +\item{item_constraints}{A list of lists specifying constraints on the +concept parameters in a two-dimensional binary model. Note that value +\code{1} in the brackets of the argument refers to the item difficulty +parameters, which in general should not be constrained. All values above +\code{1} relate to the item discrimination parameters on the single +dimensions. These should be used for constraints on concepts. Three forms +of constraints are possible: \code{conceptname = list(2, value)} will +constrain a concept to be equal to the specified value (e.g., 0) on the +first dimension of the item discrimination parameter. +\code{conceptname = list(2,"+")} will constrain the concept to be +positively scaled on the first dimension and +\code{conceptname = list(2, "-")} will constrain the concept to be +negatively scaled on the first dimension (see example). If you +wish to constrain a concept on the second dimension, please indicate this +with a \code{3} in the first position in the bracket.} + +\item{alpha_beta_start}{The starting values for the concept difficulty and +discrimination parameters in a two-dimensional binary model. Can either be +a scalar or a column vector with as many elements as the number of items +included in the scaling. If set to the default \code{NA}, starting values +will be set according to a series of probit regressions that condition the +starting values of the difficulty and discrimination parameters.} + +\item{x}{A \code{dna_scale} object.} + +\item{trim}{An integer defining the maximum length of the labels.} + +\item{object}{A \code{dna_scale} object, created by one of the scaling functions.} + +\item{type}{The type(s) of plot to generate. Must be one or more of the following: +\itemize{ + \item \code{"trace"}: For creating MCMC trace plots. + \item \code{"density"}: For creating MCMC density plots. + \item \code{"scaling"}: For creating plots summarizing the estimated parameters. +}} + +\item{parameters}{The parameter type(s) to plot. Must be one or more of the following: +\itemize{ + \item \code{"ability"}: The ability parameters for the actors, indicating their ideology. + \item \code{"discrimination"}: The item discrimination parameters. + \item \code{"difficulty"}: The item difficulty parameters. +}} + +\item{nrow}{An integer defining how many rows of parameters should be in a trace or density plot.} + +\item{ncol}{An integer defining how many columns of parameters should be in a trace or density plot.} +} +\description{ +One- or two-dimensional binary or ordinal IRT scaling for DNA. +} +\section{Overview}{ + +This set of functions applies item response theory (IRT) to discourse +networks to scale actors and concepts on an underlying ideological scale. +Four estimation functions are available: +\itemize{ + \item \code{dna_scale1dbin} Binary scaling in one dimension. + \item \code{dna_scale2dbin} Binary scaling in two dimensions. + \item \code{dna_scale1dord} Ordinal scaling in one dimension. + \item \code{dna_scale2dord} Ordinal scaling in two dimensions. +} +These functions are convenience wrappers for the \code{MCMCirt1d}, +\code{MCMCirtKd}, and \code{MCMCordfactanal} functions in the MCMCpack +package, which use Markov Chain Monte Carlo (MCMC) methods to generate +posterior samples of ability, discrimination, and difficulty parameters. + +The corresponding \code{print} function prints the formatted posterior means +and HPD intervals to the console. The autoplot function plots the MCMC trace, +density, and summaries of estimates with the help of the \pkg{ggplot2} +package. +} + +\section{Model interpretation}{ + +One parameter for each actor and two parameters for each concept are +estimated: +\itemize{ + \item Ability parameter: The estimated ability parameter indicates an + actor's ideological position on a left-right scale (e.g., industry vs. + environment, labor vs. capital, containing vs. expanding regulation etc.) + or on two dimensions. It is denoted as theta (\eqn{\theta}) in the binary + model and phi (\eqn{\phi}) in the ordinal model in this implementation. + \item Discrimination parameter: The estimated discrimination parameter + indicates a concept's ability to discriminate between actors on their + left-right scale. The discrimination parameter measures how well an item + can distinguish between actors with different levels of the latent trait + (in this case, ideology). It reflects how strongly the item is related + to the underlying trait being measured. A high discrimination parameter + means that the item is very effective at differentiating between + individuals who have slightly different levels of the underlying + ideology. For instance, a highly discriminatory question will sharply + differentiate between actors on either side of an ideological spectrum + (e.g., conservative vs. liberal). A higher discrimination value indicates + that the item is more sensitive to changes in the latent trait + (ideology). A low discrimination value suggests that the item does not + differentiate well between actors with different levels of ideology. In + the notation used in this implementation, the discrimination parameter is + denoted as beta (\eqn{\beta}) in the binary model and Lambda 2 + (\eqn{\Lambda_2}) in the ordinal model, though elsewhere in the literature + it is often denoted as alpha (\eqn{\alpha}). + \item Difficulty parameter: The difficulty parameter for a concept + represents the location of the item along the ideological spectrum. The + difficulty parameter helps determine where on the ideological spectrum a + particular item is situated. For instance, an item with a high difficulty + parameter might represent a position that only those with a + strong ideological stance (e.g., very liberal or very conservative) are + likely to endorse. An item with a low difficulty parameter would be + endorsed by most actors, indicating that the statement is relatively easy + to agree with (possibly a moderate or widely accepted stance). + Conversely, an item with a high difficulty parameter would only be + endorsed by those with a more extreme position on the ideology being + measured. In the notation used in this implementation, the difficulty + parameter is denoted as alpha (\eqn{\alpha}) in the binary model and as + Lambda 1 (\eqn{\Lambda_1}) in the ordinal model, though elsewhere in the + literature it is often denoted as beta (\eqn{\beta}). +} +See the help pages of the \code{MCMCirt1d} function (for the binary model) +and the \code{MCMCordfactanal} (for the ordinal model) for details on the +functional form and parameterization of the mdoel, which is a slight +deviation from the standard 2PL model. +} + +\section{Variable coding}{ + +As in a two-mode network in \link{dna_network}, two variables have to be +provided for the scaling. The first variable corresponds to the rows of a +two-mode network and usually entails actors (e.g., \code{"organizations"}), +while the second variable is equal to the columns of a two-mode network, +typically expressed by \code{"concepts"}. The \code{dna_scale} functions +use \code{"actors"} and \code{"concepts"} as synonyms for \code{variable1} +and \code{variable2}. However, the scaling is not restricted to +\code{"actors"} and \code{"concepts"} but depends on what you provide in +\code{variable1} or \code{variable2}. +} + +\section{Binary models}{ + +Binary models recode two-mode network matrices into zeroes and ones and then +estimate a logit model for binary data. The network cells are recoded using +the following rules: +\itemize{ + \item For a binary qualifier, \code{dna_scale1dbin} internally uses the + \code{combine} qualifier aggregation and then recodes the values into + \code{0} for disagreement, \code{1} for agreement, and \code{NA} for + mixed positions and non-mentions of concepts. If + \code{zero_as_na = FALSE} is set, the mixed positions and non-mentions + become \code{0} instead of \code{NA} and are treated as informative. + \item If no qualifier is used or the qualifier variable is categorical, + non-mentions become \code{0} and any number of mentions become \code{1}. + \code{zero_as_na} must be \code{FALSE} in this case. + \item If a threshold is used (e.g., \code{0.4}), the fraction of positive + mentions over the sum of both positive and negative mentions, which + scales between \code{0} and \code{1}, is used to recode fractions smaller + than or equal to the threshold as \code{0}, values larger than or equal + to one minus the threshold as \code{1}, and values between the threshold + and one minus the threshold as \code{NA}. For example, if an actor + mentions a concept six times in a positive way and five times in a + negative way, the fraction of positive mentions is \code{6 / 11 = 0.54}. + If a threshold of \code{0.4} is used (or, equivalently, \code{0.6}), the + value is recoded to \code{NA}. If no threshold is used, the value becomes + \code{1}. + \item Integer qualifiers are also recoded into \code{0} and \code{1} by + rescaling the qualifier values between \code{0} and \code{1}. Thresholds + larger than \code{0} and smaller than \code{1} are possible here as well. +} +} + +\section{Ordinal models}{ + +Ordinal models recode two-mode network matrices into values \code{1}, +\code{2}, or \code{3} and then estimate an ordinal latent factor model for +ordinal data, which corresponds to ordinal item response theory. The network +cells are recoded using the following rules: +\itemize{ + \item For a binary qualifier, \code{dna_scale1dord} internally uses the + \code{combine} qualifier aggregation and then recodes the values into + \code{1} for disagreement, \code{2} for ambivalent positions with both + positive and negative mentions, \code{3} for exclusively positive + mentions, and \code{NA} for non-mentions. If \code{zero_as_na = FALSE} is + set, non-mentions are recoded as \code{2} as well and thereby become + informative as neutral positions. + \item If no qualifier is used or the qualifier variable is categorical, + non-mentions become \code{0} and any number of mentions become \code{1}. + \code{zero_as_na} must be \code{FALSE} in this case. + \item If a threshold is used, the same recoding procedure as in the binary + model is used, but values below or equal to the threshold become + \code{1}, values above the threshold and below one minus the threshold + become \code{2}, values equal to or above the threshold become \code{3}, + and non-mentions are coded as \code{NA} (unless + \code{zero_as_na = FALSE}). + \item Integer qualifiers are also recoded into three positive integer + values by rescaling the qualifier values between \code{0} and \code{1}. + Thresholds larger than \code{0} and smaller than \code{1} are possible + here as well. +} +In ordinal models, threshold parameters are estimated alongside the other +parameters, like in other ordinal logit models. They are treated as nuisance +parameters and not reported in the output. However, they are stored in the +object as part of the posterior samples and can be retrieved if necessary. +} + +\section{Tweaking the estimation}{ + +As these functions implement a Bayesian Item Response Theory approach, +\code{priors} and \code{starting values} can be set on the actor and concept +parameters. Changing the default \code{prior} values can often help you to +achieve better results. Constraints on the actor parameters can also be +specified to help identifying the model and to indicate in which direction +ideological positions of actors and concepts run. The returned MCMC output +can also be post-processed by normalizing the samples for each iteration with +\code{mcmc_normalize}. Normalization can be a sufficient way of identifying +one-dimensional ideal point models. + +Unlike \link{dna_scale1dbin}, \link{dna_scale2dbin} constrains the values +indicated in \code{variable2}. For these values, the scaling estimates an +item discrimination parameter for each dimension and an item difficulty +parameter for both dimensions. The item difficulty parameter should, +however, not be constrained (see \link[MCMCpack]{MCMCirtKd}). Therefore, you +should set constraints on the item discrimination parameters. + +Fitting two-dimensional scaling models requires a good choice of concept +constraints to specify the ideological dimensions of your data. A suitable +way of identifying your ideological dimensions is to constrain one item +discrimination parameter to load only on one dimension. This means that we +set one parameter to load either positive or negative on one dimension and +setting it to zero on the other. A second concept should also be constrained +to load either positive or negative on one dimension (see example). + +The argument \code{drop_min_actors} excludes actors with only a limited +number of concepts used. Limited participation of actors in a debate can +impact the scaling of the ideal points, as actors with only few mentions of +concepts convey limited information on their ideological position. The same +can also be done for concepts with the argument \code{drop_min_concepts}. +Concepts that have been rarely mentioned do not strongly discriminate the +ideological positions of actors and can, therefore, impact the accuracy of +the scaling. Reducing the number of actors of concepts to be scaled hence +improves the precision of the ideological positions for both variables and +the scaling itself. Another possibility to reduce the number of concepts is +to use \code{drop_constant_concepts}, which will reduce concepts not having +any variation in the agreement/disagreement structure of actors. This means +that all concepts will be dropped which have only agreeing or disagreeing +statements. +} + +\examples{ +\dontrun{ +library("rDNA") +library("ggplot2") +library("ggrepel") + +dna_init() +dna_openDatabase(dna_sample(overwrite = TRUE), coderPassword = "sample") + +# one-dimensional binary model +fit_1d_bin <- dna_scale1dbin( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + threshold = 0.49, + theta_constraints = list( + `National Petrochemical & Refiners Association` = "+", + `Alliance to Save Energy` = "-"), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_normalize = TRUE, + theta_prior_mean = 0, + theta_prior_variance = 1, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.25, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + verbose = TRUE, + seed = 12345 +) +fit_1d_bin +autoplot(fit_1d_bin) + +# two-dimensional binary model +fit_2d_bin <- dna_scale2dbin( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + threshold = 0.4, + item_constraints = list( + `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), + `Climate change is caused by greenhouse gases (CO2).` = c(3, 0), + `CO2 legislation will not hurt the economy.` = list(3, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 1, + store_variables = "organization", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 +) +fit_2d_bin +autoplot(fit_2d_bin) + +# one-dimensional ordinal model +fit_1d_ord <- dna_scale1dord( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = 0.4, + lambda_constraints = list(`CO2 legislation will not hurt the economy.` = list(2, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_tune = 1.5, + mcmc_normalize = FALSE, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "organization", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 +) +fit_1d_ord +autoplot(fit_1d_ord) + +# two-dimensional ordinal model +fit_2d_ord <- dna_scale2dord( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = 0.4, + lambda_constraints = list( + `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), + `Climate change is caused by greenhouse gases (CO2).` = list(3, 0), + `CO2 legislation will not hurt the economy.` = list(3, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_tune = 1.5, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 +) +fit_2d_ord +autoplot(fit_2d_ord) +} + +} +\author{ +Tim Henrichsen, Philip Leifeld, Johannes B. Gruber +} diff --git a/rDNA/rDNA/tests/testthat/test-scaling.R b/rDNA/rDNA/tests/testthat/test-scaling.R new file mode 100644 index 00000000..8ee50e03 --- /dev/null +++ b/rDNA/rDNA/tests/testthat/test-scaling.R @@ -0,0 +1,192 @@ +context("Testing IRT scaling") + +# Create a function to set up the database for tests +setup_dna_database <- function() { + dna_init() + samp <- dna_sample() + dna_openDatabase(samp, coderId = 1, coderPassword = "sample") + return(samp) +} + +# Create a function to clean up after tests +cleanup_dna_database <- function(samp) { + dna_closeDatabase() + unlink(samp) +} + +test_that("dna_scale1dbin produces expected output with help file example", { + testthat::skip_on_cran() + testthat::skip_on_ci() + samp <- setup_dna_database() + + sink(nullfile()) + fit_1d_bin <- dna_scale1dbin( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + threshold = 0.49, + theta_constraints = list( + `National Petrochemical & Refiners Association` = "+", + `Alliance to Save Energy` = "-"), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_normalize = TRUE, + theta_prior_mean = 0, + theta_prior_variance = 1, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 0.25, + store_variables = "both", + drop_constant_concepts = FALSE, + drop_min_actors = 1, + verbose = TRUE, + seed = 12345 + ) + sink() + + expect_contains(class(fit_1d_bin), "dna_scale") + expect_contains(class(fit_1d_bin), "dna_scale1dbin") + expect_equal(names(fit_1d_bin), c("sample", "ability", "discrimination", "difficulty", "call")) + expect_equal(dim(fit_1d_bin$sample), c(2000, 17)) + expect_equal(dim(fit_1d_bin$ability), c(7, 9)) + expect_equal(dim(fit_1d_bin$discrimination), c(5, 8)) + expect_equal(dim(fit_1d_bin$difficulty), c(5, 8)) + expect_length(utils::capture.output(fit_1d_bin), 27) + plot_1d_bin <- autoplot(fit_1d_bin) + expect_length(plot_1d_bin, 9) + classes <- unique(sapply(plot_1d_bin, class)[2, ]) + expect_length(classes, 1) + expect_equal(classes, "ggplot") + + cleanup_dna_database(samp) +}) + +test_that("dna_scale2dbin produces expected output with help file example", { + testthat::skip_on_cran() + testthat::skip_on_ci() + samp <- setup_dna_database() + + sink(nullfile()) + fit_2d_bin <- dna_scale2dbin( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + threshold = 0.4, + item_constraints = list( + `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), + `Climate change is caused by greenhouse gases (CO2).` = c(3, 0), + `CO2 legislation will not hurt the economy.` = list(3, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + alpha_beta_prior_mean = 0, + alpha_beta_prior_variance = 1, + store_variables = "organization", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 + ) + sink() + + expect_contains(class(fit_2d_bin), "dna_scale") + expect_contains(class(fit_2d_bin), "dna_scale2dbin") + expect_equal(names(fit_2d_bin), c("sample", "ability", "call")) + expect_equal(dim(fit_2d_bin$sample), c(2000, 14)) + expect_equal(dim(fit_2d_bin$ability), c(7, 12)) + expect_length(utils::capture.output(fit_2d_bin), 11) + plot_2d_bin <- autoplot(fit_2d_bin) + expect_length(plot_2d_bin, 3) + classes <- unique(sapply(plot_2d_bin, class)[2, ]) + expect_length(classes, 1) + expect_equal(classes, "ggplot") + + cleanup_dna_database(samp) +}) + +test_that("dna_scale1dord produces expected output with help file example", { + testthat::skip_on_cran() + testthat::skip_on_ci() + samp <- setup_dna_database() + + sink(nullfile()) + expect_warning(fit_1d_ord <- dna_scale1dord( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = 0.4, + lambda_constraints = list(`CO2 legislation will not hurt the economy.` = list(2, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_tune = 1.5, + mcmc_normalize = FALSE, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "organization", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 + ), "Setting 'zero_as_na' to FALSE because there are otherwise only 1s in the data matrix") + sink() + + expect_contains(class(fit_1d_ord), "dna_scale") + expect_contains(class(fit_1d_ord), "dna_scale1dord") + expect_equal(names(fit_1d_ord), c("sample", "ability", "call")) + expect_equal(dim(fit_1d_ord$sample), c(2000, 7)) + expect_equal(dim(fit_1d_ord$ability), c(7, 9)) + expect_length(utils::capture.output(fit_1d_ord), 11) + plot_1d_ord <- autoplot(fit_1d_ord) + expect_length(plot_1d_ord, 3) + classes <- unique(sapply(plot_1d_ord, class)[2, ]) + expect_length(classes, 1) + expect_equal(classes, "ggplot") + + cleanup_dna_database(samp) +}) + +test_that("dna_scale2dord produces expected output with help file example", { + testthat::skip_on_cran() + testthat::skip_on_ci() + samp <- setup_dna_database() + + sink(nullfile()) + expect_warning(fit_2d_ord <- dna_scale2dord( + variable1 = "organization", + variable2 = "concept", + qualifier = "agreement", + zero_as_na = TRUE, + threshold = 0.4, + lambda_constraints = list( + `Climate change is caused by greenhouse gases (CO2).` = list(2, "-"), + `Climate change is caused by greenhouse gases (CO2).` = list(3, 0), + `CO2 legislation will not hurt the economy.` = list(3, "-")), + mcmc_iterations = 20000, + mcmc_burnin = 2000, + mcmc_thin = 10, + mcmc_tune = 1.5, + lambda_prior_mean = 0, + lambda_prior_variance = 0.1, + store_variables = "both", + drop_constant_concepts = TRUE, + verbose = TRUE, + seed = 12345 + ), "'threshold' is not supported and will be ignored.") + sink() + + expect_contains(class(fit_2d_ord), "dna_scale") + expect_contains(class(fit_2d_ord), "dna_scale2dord") + expect_equal(names(fit_2d_ord), c("sample", "ability", "discrimination", "difficulty", "call")) + expect_equal(dim(fit_2d_ord$sample), c(2000, 28)) + expect_equal(dim(fit_2d_ord$ability), c(7, 12)) + expect_equal(dim(fit_2d_ord$discrimination), c(5, 11)) + expect_equal(dim(fit_2d_ord$difficulty), c(5, 8)) + expect_length(utils::capture.output(fit_2d_ord), 27) + plot_2d_ord <- autoplot(fit_2d_ord) + expect_length(plot_2d_ord, 9) + classes <- unique(sapply(plot_2d_ord, class)[2, ]) + expect_length(classes, 1) + expect_equal(classes, "ggplot") + + cleanup_dna_database(samp) +}) From 6f6639da2c1ba2657e8709b760b713ae380b5300 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 17:38:27 +0200 Subject: [PATCH 23/43] Updated gradle, yml, rDNA tests+check --- .../{DNA build.yml => build-and-check.yml} | 129 ++++++++------- bibliography/bibliography.bib | 9 ++ bibliography/build.gradle | 28 +++- build/bibliography.md | 147 ++++++++++-------- dna/build.gradle | 20 ++- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 44 ++++-- gradlew.bat | 37 +++-- rDNA/build.gradle | 140 +++++++++++++---- rDNA/rDNA/.Rbuildignore | 1 - rDNA/rDNA/DESCRIPTION | 8 +- rDNA/rDNA/LICENSE | 4 + rDNA/rDNA/R/rDNA.R | 86 +++++----- rDNA/rDNA/inst/CITATION | 34 ++-- .../rDNA/man/as.matrix.dna_network_onemode.Rd | 4 +- .../rDNA/man/as.matrix.dna_network_twomode.Rd | 4 +- rDNA/rDNA/man/autoplot.dna_network.Rd | 4 +- rDNA/rDNA/man/dna_api.Rd | 4 +- rDNA/rDNA/man/dna_closeDatabase.Rd | 4 +- rDNA/rDNA/man/dna_getAttributes.Rd | 2 +- rDNA/rDNA/man/dna_getHeadlessDna.Rd | 4 +- rDNA/rDNA/man/dna_init.Rd | 4 +- rDNA/rDNA/man/dna_jar.Rd | 28 ++-- rDNA/rDNA/man/dna_network.Rd | 4 +- rDNA/rDNA/man/dna_openConnectionProfile.Rd | 4 +- rDNA/rDNA/man/dna_openDatabase.Rd | 4 +- rDNA/rDNA/man/dna_phaseTransitions.Rd | 4 +- rDNA/rDNA/man/dna_printDetails.Rd | 4 +- rDNA/rDNA/man/dna_sample.Rd | 4 +- rDNA/rDNA/man/dna_saveConnectionProfile.Rd | 4 +- rDNA/rDNA/man/dna_tidygraph.Rd | 4 +- rDNA/rDNA/man/print.dna_network_onemode.Rd | 4 +- rDNA/rDNA/man/print.dna_network_twomode.Rd | 4 +- rDNA/rDNA/tests/testthat/test-clustering.R | 12 +- .../tests/testthat/test-phasetransitions.R | 49 +++--- rDNA/rDNA/tests/testthat/test-scaling.R | 10 +- rDNA/rDNA/tests/testthat/test-zzz.R | 2 +- 38 files changed, 538 insertions(+), 324 deletions(-) rename .github/workflows/{DNA build.yml => build-and-check.yml} (53%) create mode 100644 rDNA/rDNA/LICENSE diff --git a/.github/workflows/DNA build.yml b/.github/workflows/build-and-check.yml similarity index 53% rename from .github/workflows/DNA build.yml rename to .github/workflows/build-and-check.yml index 30cbea9e..fd136281 100644 --- a/.github/workflows/DNA build.yml +++ b/.github/workflows/build-and-check.yml @@ -3,41 +3,74 @@ name: build on: [push, pull_request, workflow_dispatch] jobs: - Bibliography: + install-dependencies: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: r-lib/actions/setup-tinytex@v2 + - uses: actions/checkout@v3 - - name: Install LaTeX packages - run: tlmgr install urlbst + - name: Restore System Dependencies Cache + uses: actions/cache@v3 + id: system-deps-cache + with: + path: | + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + /usr/local/texlive/tlpkg/texlive.tlpdb + key: ${{ runner.os }}-system-deps-v1 + + - name: Install System Dependencies + if: steps.system-deps-cache.outputs.cache-hit != 'true' + run: | + sudo apt-get update + sudo apt-get install -y texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-bibtex-extra pandoc pandoc-citeproc + + - uses: actions/cache@v3 + id: r-packages-cache + with: + path: | + ~/.cache/R + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/cache@v3 - id: pandoc - with: - path: pandoc-2.19.2-1 - key: pandoc-2.19.2-1 - - name: Download pandoc-2.19.2-1-amd64.deb - if: steps.pandoc.outputs.cache-hit != 'true' - run: | - mkdir pandoc-2.19.2-1 - cd pandoc-2.19.2-1 - wget https://github.com/jgm/pandoc/releases/download/2.19.2/pandoc-2.19.2-1-amd64.deb - - name: Install pandoc - run: | - cd pandoc-2.19.2-1 - sudo dpkg -i pandoc-2.19.2-1-amd64.deb + - uses: r-lib/actions/setup-r@v2 - - name: Check pandoc version - run: pandoc --version + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + cache-version: 2 + working-directory: rDNA/rDNA + extra-packages: | + any::devtools + any::testthat + any::ggplot2 + any::roxygen2 + any::igraph + any::ggraph + any::askpass + any::cluster + any::sna + any::ggrepel + any::coda + any::MCMCpack + any::tidygraph + any::heatmaply + any::factoextra + any::MASS + any::pbmcapply + Bibliography: + needs: install-dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: r-lib/actions/setup-tinytex@v2 + - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 - - name: Execute Gradle build for bibliography run: ./gradlew :bibliography:build - - name: Store artifacts uses: actions/upload-artifact@v3 with: @@ -68,33 +101,16 @@ jobs: path: build/*.jar rDNA: + needs: [DNA, install-dependencies] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: r-lib/actions/setup-r@v2 - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - cache-version: 2 - working-directory: rDNA/rDNA - extra-packages: | - any::ggplot2 - any::roxygen2 - any::igraph - any::ggraph - any::askpass - any::cluster - any::sna - - uses: gradle/wrapper-validation-action@v1 - - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 - - name: Execute Gradle build for rDNA run: ./gradlew :rDNA:build - - name: Store artifacts uses: actions/upload-artifact@v3 with: @@ -103,9 +119,8 @@ jobs: rDNA-check: runs-on: ${{ matrix.config.os }} - + needs: rDNA name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - strategy: fail-fast: false matrix: @@ -115,29 +130,39 @@ jobs: - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} - env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes - steps: - uses: actions/checkout@v3 - - - uses: r-lib/actions/setup-pandoc@v2 - - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck needs: check working-directory: rDNA/rDNA - - uses: r-lib/actions/check-r-package@v2 with: working-directory: rDNA/rDNA upload-snapshots: true + + rDNA-test: + runs-on: ubuntu-latest + needs: [rDNA, install-dependencies] + steps: + - uses: actions/checkout@v3 + - uses: r-lib/actions/setup-r@v2 + - uses: gradle/wrapper-validation-action@v1 + - name: Setup Gradle + uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle rDNATest Task + run: ./gradlew :rDNA:rDNATest + - name: Store test results + uses: actions/upload-artifact@v3 + with: + name: rDNA-Test-Results + path: build/reports/tests diff --git a/bibliography/bibliography.bib b/bibliography/bibliography.bib index a07f43f5..bb59756b 100644 --- a/bibliography/bibliography.bib +++ b/bibliography/bibliography.bib @@ -2828,6 +2828,15 @@ @article{starke2023green doi={10.1016/j.jclepro.2023.139058} } +@article{steemers2024united, + title={The {U}nited {K}ingdom's {R}ejoin Movement: A Post-{B}rexit Analysis of Framing Strategies}, + author={Steemers, Kai}, + journal={British Journal of Politics and International Relations}, + year={2024}, + doi={10.1177/13691481241269308}, + note={Forthcoming} +} + @article{steinfeld2016f, title={The {F}-Campaign: A Discourse Network Analysis of Party Leaders' Campaign Statements on {F}acebook}, author={Steinfeld, Nili}, diff --git a/bibliography/build.gradle b/bibliography/build.gradle index 537c685d..a48cc1f5 100644 --- a/bibliography/build.gradle +++ b/bibliography/build.gradle @@ -1,3 +1,7 @@ +plugins { + id 'base' +} + task bibliographyMarkdown { inputs.dir '.' doLast { @@ -47,7 +51,7 @@ task bibliographyPdflatex { } } -task build { +build { dependsOn bibliographyMarkdown dependsOn bibliographyPdflatex inputs.dir '.' @@ -56,3 +60,25 @@ task build { doLast {} } + +clean { + doFirst { + println "Attempting to delete bibliography files..." + + def filesToDelete = fileTree(dir: "$rootDir/build", include: '**/bibliography.*').files + if (filesToDelete.isEmpty()) { + println "No bibliography files found to delete." + } else { + println "Found bibliography files to delete: " + filesToDelete.each { file -> + println file.absolutePath + } + } + } + + delete fileTree(dir: "$rootDir/build", include: '**/bibliography.*') + + doLast { + println "Bibliography files have been removed." + } +} diff --git a/build/bibliography.md b/build/bibliography.md index dc3aaec0..c228f836 100644 --- a/build/bibliography.md +++ b/build/bibliography.md @@ -4,7 +4,7 @@ author: bibliography: - bibliography.bib csl: apa-numeric-superscript-brackets.csl -date: 2024-08-10 +date: 2024-08-19 title: "Discourse Network Analysis: Bibliography" --- @@ -2462,9 +2462,18 @@ conflicts. *Journal of Cleaner Production*, *425*, 139058.
+
+ +266\. Steemers, K. (2024). The United Kingdom’s Rejoin movement: A +post-Brexit analysis of framing strategies. *British Journal of Politics +and International Relations*. + + +
+
-266\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis +267\. Steinfeld, N. (2016). The F-campaign: A discourse network analysis of party leaders’ campaign statements on Facebook. *Israel Affairs*, *22*(3–4), 743–759. @@ -2472,7 +2481,7 @@ of party leaders’ campaign statements on Facebook. *Israel Affairs*,
-267\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions +268\. St-Jacques, B. (2019). *Us and them. Mapping discourse coalitions in the EU copyright directive debate* \[Master's Thesis, Hertie School of Governance, Master of Public Policy\]. @@ -2481,7 +2490,7 @@ of Governance, Master of Public Policy\].
-268\. Stoddart, M. C. J., Koop-Monteiro, Y., & Tindall, D. B. (2024). +269\. Stoddart, M. C. J., Koop-Monteiro, Y., & Tindall, D. B. (2024). Instagram as an arena of climate change communication and mobilization: A discourse network analysis of COP26. *Environmental Communication*. @@ -2490,7 +2499,7 @@ A discourse network analysis of COP26. *Environmental Communication*.
-269\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial +270\. Stoddart, M. C. J., Mattoni, A., & McLevey, J. (2020). *Industrial development and eco-tourisms. Can oil extraction and nature conservation co-exist?* Palgrave Macmillan. @@ -2499,7 +2508,7 @@ co-exist?* Palgrave Macmillan.
-270\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. +271\. Stoddart, M. C. J., McCurdy, P., Slawinski, N., & Collins, C. G. (2020). Envisioning energy futures in the North Atlantic oil industry: Avoidance, persistence, and transformation as responses to climate change. *Energy Research & Social Science*, *69*, 101662. @@ -2509,7 +2518,7 @@ change. *Energy Research & Social Science*, *69*, 101662.
-271\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented +272\. Stoddart, M. C. J., & Nezhadhossein, E. (2016). Is nature-oriented tourism a pro-environmental practice? Examining tourism–environmentalism alignments through discourse networks and intersectoral relationships. *The Sociological Quarterly*, *57*(3), 544–568. @@ -2519,7 +2528,7 @@ alignments through discourse networks and intersectoral relationships.
-272\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. +273\. Stoddart, M. C. J., Ramos, H., Foster, K., & YlĂ€-Anttila, T. (2023). Competing crises? Media coverage and framing of climate change during the COVID-19 pandemic. *Environmental Communication*, *17*(3), 276–292. @@ -2528,7 +2537,7 @@ during the COVID-19 pandemic. *Environmental Communication*, *17*(3),
-273\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the +274\. Stoddart, M. C. J., & Smith, J. (2016). The endangered arctic, the arctic as resource frontier: Canadian news media narratives of climate change and the north. *Canadian Review of Sociology/Revue Canadienne de Sociologie*, *53*(3), 316–336. @@ -2537,7 +2546,7 @@ Sociologie*, *53*(3), 316–336.
-274\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media +275\. Stoddart, M. C. J., & Tindall, D. B. (2015). Canadian news media and the cultural dynamics of multilevel climate governance. *Environmental Politics*, *24*(3), 401–422. @@ -2546,7 +2555,7 @@ and the cultural dynamics of multilevel climate governance.
-275\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. +276\. Stoddart, M. C. J., Tindall, D. B., Smith, J., & Haluza-Delay, R. (2017). Media access and political efficacy in the eco-politics of climate change: Canadian national news and mediated policy networks. *Environmental Communication*, *11*(3), 386–400. @@ -2556,7 +2565,7 @@ climate change: Canadian national news and mediated policy networks.
-276\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of +277\. Stoddart, M. C. J., & Yang, Y. (2022). What are the roles of regional and local climate governance discourse and actors? Mediated climate change policy networks in Atlantic Canada. *Review of Policy Research*, *40*(6), 1144–1168. @@ -2565,7 +2574,7 @@ Research*, *40*(6), 1144–1168.
-277\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat +278\. Sumirat, P. A., & Eriyanto, E. (2023). Koalisi wacana dalam debat pemekaran Papua: Analisis jaringan wacana debat pemekaran tiga provinsi Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16. @@ -2574,7 +2583,7 @@ Baru di Papua. *Jurnal Riset Komunikasi (JURKOM)*, *6*(2), 1–16.
-278\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The +279\. Swacha, P., Karaczun, Z. M., & Murawska, D. (2022). The Europeanization of Polish climate policy. *Ekonomia i ƚrodowisko – Economics and Environment*, *83*(4), 62–75. @@ -2583,7 +2592,7 @@ Economics and Environment*, *83*(4), 62–75.
-279\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and +280\. Swarnakar, P., Shukla, R., & Broadbent, J. (2022). Beliefs and networks: Mapping the Indian climate policy discourse surrounding the Paris climate change conference in 2015. *Environmental Communication*, *16*(2), 145–162. @@ -2592,7 +2601,7 @@ Paris climate change conference in 2015. *Environmental Communication*,
-280\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre +281\. Swedenmark, S. Ö. (2018). *“Vi ska strĂ€cka oss lite lĂ€ngre Ă€n vi behöver.” En fallstudie om diskursen kring mellanorganisatorisk samverkan inom Stockholmsregionen* \[Magisteruppsats i statsvetenskap, Mittuniversitetet\]. @@ -2602,7 +2611,7 @@ kring mellanorganisatorisk samverkan inom Stockholmsregionen*
-281\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in +282\. Swinkels, E. M. (2021). *The role of EU leaders and ideas in managing the Eurozone crisis: Navigating uncharted territory* \[PhD thesis, Utrecht University\]. @@ -2610,7 +2619,7 @@ thesis, Utrecht University\].
-282\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking +283\. Swinkels, M., & Esch, F. van. (2022). Deciding upon the banking union: How a joint belief shift instigated deep institutional change in Eurozone governance. *European Policy Analysis*, *8*(1), 9–32. @@ -2619,7 +2628,7 @@ Eurozone governance. *European Policy Analysis*, *8*(1), 9–32.
-283\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the +284\. Syafrudin, M., Sarwono, Hakim, A., & Solimun. (2023). Examine the elements that impact food security. *Proceedings of the Fifth Annual International Conference on Business and Public Administration (AICoBPA 2022)*, 563–581. @@ -2628,7 +2637,7 @@ International Conference on Business and Public Administration (AICoBPA
-284\. Taranger, K. K. (2020). *The institutionalisation of climate +285\. Taranger, K. K. (2020). *The institutionalisation of climate justice in the global governance architecture* \[Master's Thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development, Environmental Governance Section\]. @@ -2637,7 +2646,7 @@ Environmental Governance Section\].
-285\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping +286\. Tobin, P., Schmidt, N. M., Tosun, J., & Burns, C. (2018). Mapping states’ Paris climate pledges: Analysing targets and groups at COP 21. *Global Environmental Change*, *48*, 11–21. @@ -2646,7 +2655,7 @@ states’ Paris climate pledges: Analysing targets and groups at COP 21.
-286\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate +287\. Tolstukha, E. (2022). *Stalemate in the democratic reform debate of the European Union? A dynamic discourse network analysis of actors and their commitment to reform options* \[PhD thesis, University of Glasgow, School of Social; Political Sciences\]. @@ -2656,7 +2665,7 @@ Glasgow, School of Social; Political Sciences\].
-287\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing +288\. Tosun, J., & Lang, A. (2016). The politics of hydraulic fracturing in Germany: Party competition at different levels of government. In C. M. Weible, T. Heikkila, K. Ingold, & M. Fischer (Eds.), *Policy debates on hydraulic fracturing. Comparing coalition politics in North America @@ -2667,7 +2676,7 @@ and Europe* (pp. 177–200). Palgrave Macmillan.
-288\. Tosun, J., & Schaub, S. (2017). Mobilization in the European +289\. Tosun, J., & Schaub, S. (2017). Mobilization in the European public sphere: The struggle over genetically modified organisms. *Review of Policy Research*, *34*(3), 310–330. @@ -2676,7 +2685,7 @@ of Policy Research*, *34*(3), 310–330.
-289\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej +290\. TribulovĂĄ, Z. (2019). *Postoj českej republiky k energetickej tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2685,7 +2694,7 @@ tranzĂ­cii v kontexte energiewende – analĂœza politickĂ©ho diskurzu*
-290\. Tuinenburg, J. (2019). *The effect of discourse networks on the +291\. Tuinenburg, J. (2019). *The effect of discourse networks on the leading support schemes for renewable electricity* \[Master's Thesis, Universiteit Utrecht, Sustainable Development, Earth System Governance\]. @@ -2694,7 +2703,7 @@ Governance\].
-291\. Umansky Casapa, N. (2022). *Securitization and social media +292\. Umansky Casapa, N. (2022). *Securitization and social media networks: Who tweets security?* \[Doctoral Thesis, University College Dublin, School of Politics; International Relations\]. @@ -2703,7 +2712,7 @@ Dublin, School of Politics; International Relations\].
-292\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza +293\. VankovĂĄ, L. (2019). *Ć€aĆŸba hnedĂ©ho uhlia na hornej nitre: Analỳza diskurzĂ­vnych sietĂ­* \[Master's Thesis, Masaryk University, Faculty of Social Studies\]. @@ -2711,7 +2720,7 @@ Social Studies\].
-293\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level +294\. Vantaggiato, F. P., & Lubell, M. N. (2024). *Governing sea level rise in a polycentric system. Easier said than done*. Cambridge University Press. @@ -2719,7 +2728,7 @@ University Press.
-294\. Vaughan, M. (2020). Talking about tax: The discursive distance +295\. Vaughan, M. (2020). Talking about tax: The discursive distance between 38 Degrees and GetUp. *Journal of Information Technology & Politics*, *17*(2), 114–129. @@ -2728,7 +2737,7 @@ Politics*, *17*(2), 114–129.
-295\. Vedres, B. (2022). Multivocality and robust action dynamics in +296\. Vedres, B. (2022). Multivocality and robust action dynamics in political discourse. *Poetics*, *90*, 101576. @@ -2736,7 +2745,7 @@ political discourse. *Poetics*, *90*, 101576.
-296\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet +297\. Vesa, J., Gronow, A., & YlÀ-Anttila, T. (2020). The quiet opposition: How the pro-economy lobby influences climate policy. *Global Environmental Change*, *63*, 102117. @@ -2745,7 +2754,7 @@ Environmental Change*, *63*, 102117.
-297\. Vogeler, C. S. (2022). The integration of environmental objectives +298\. Vogeler, C. S. (2022). The integration of environmental objectives in the common agricultural policy – partisan politics in the European Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 551–569. @@ -2754,7 +2763,7 @@ Parliament. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-298\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting +299\. Vogeler, C. S., Möck, M., & Bandelow, N. C. (2021). Shifting governance cooperatively – coordination by public discourses in the German water–food nexus. *Journal of Environmental Management*, *286*, 112266. @@ -2763,7 +2772,7 @@ German water–food nexus. *Journal of Environmental Management*, *286*,
-299\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. +300\. Vogeler, C. S., Schwindenhammer, S., Gonglach, D., & Bandelow, N. C. (2021). Agri-food technology politics: Exploring policy narratives in the European Parliament. *European Policy Analysis*, *7*, 324–343. @@ -2772,7 +2781,7 @@ the European Parliament. *European Policy Analysis*, *7*, 324–343.
-300\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of +301\. Völker, T., & Saldivia Gonzatti, D. (2024). Discourse networks of the far right: How far-right actors become mainstream in public debates. *Political Communication*, *41*(3), 353–372. @@ -2781,7 +2790,7 @@ the far right: How far-right actors become mainstream in public debates.
-301\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., +302\. Steinsdorff, S. von, Gottmann, L., HĂŒggelmeyer, M., Jeske, I.-M., Onkelbach, C., & Siebeking, J. (2021). Plenardebatten als Spiegel sich wandelnder Diskurskoalitionen: Die Positionierung der Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977. @@ -2792,7 +2801,7 @@ Bundestagsfraktionen zum VerhĂ€ltnis von Ökologie und Ökonomie seit 1977.
-302\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of +303\. Wagemans, S. (2023). *A shadow on solar mobility: The impact of failure on socio-technical system configurations* \[Master’s thesis, Universiteit Utrecht, Copernicus Institute of Sustainable Development\]. @@ -2801,7 +2810,7 @@ Universiteit Utrecht, Copernicus Institute of Sustainable Development\].
-303\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse +304\. Wagner, P., & Payne, D. (2017). Trends, frames and discourse networks: Analysing the coverage of climate change in Irish newspapers. *Irish Journal of Sociology*, *25*(1), 5–28. @@ -2810,7 +2819,7 @@ networks: Analysing the coverage of climate change in Irish newspapers.
-304\. Wallaschek, S. (2019). The discursive appeal to solidarity and +305\. Wallaschek, S. (2019). The discursive appeal to solidarity and partisan journalism in Europe’s migration crisis. *Social Inclusion*, *7*(2), 187–197. @@ -2818,7 +2827,7 @@ partisan journalism in Europe’s migration crisis. *Social Inclusion*,
-305\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse +306\. Wallaschek, S. (2019). *Mapping solidarity in Europe. Discourse networks in the Euro crisis and Europe’s migration crisis* \[Doctoral Dissertation, University of Bremen, Bremen International Graduate School of Social Sciences (BIGSSS), Department of Social Sciences\]. @@ -2828,7 +2837,7 @@ of Social Sciences (BIGSSS), Department of Social Sciences\].
-306\. Wallaschek, S. (2020). Analyzing the European parliamentary +307\. Wallaschek, S. (2020). Analyzing the European parliamentary elections in 2019: Actor visibility and issue-framing in transnational media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl 2019. Ringen um die Zukunft Europas* (pp. 219–230). Springer VS. @@ -2838,7 +2847,7 @@ media. In M. Kaeding, M. MĂŒller, & J. SchmĂ€lter (Eds.), *Die Europwahl
-307\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and +308\. Wallaschek, S. (2020). Contested solidarity in the Euro crisis and Europe’s migration crisis: A discourse network analysis. *Journal of European Public Policy*, *27*(7), 1034–1053. @@ -2847,7 +2856,7 @@ European Public Policy*, *27*(7), 1034–1053.
-308\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A +309\. Wallaschek, S. (2020). Framing solidarity in the Euro crisis: A comparison of the German and Irish media discourse. *New Political Economy*, *25*(2), 231–247. @@ -2856,7 +2865,7 @@ Economy*, *25*(2), 231–247.
-309\. Wallaschek, S. (2020). The discursive construction of solidarity: +310\. Wallaschek, S. (2020). The discursive construction of solidarity: Analysing public claims in Europe’s migration crisis. *Political Studies*, *68*(1), 74–92. @@ -2864,7 +2873,7 @@ Studies*, *68*(1), 74–92.
-310\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration +311\. Wallaschek, S. (2017). Notions of solidarity in Europe’s migration crisis: The case of Germany’s media discourse. *EuropeNow Journal*, *11*. @@ -2872,7 +2881,7 @@ crisis: The case of Germany’s media discourse. *EuropeNow Journal*,
-311\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., +312\. Wallaschek, S., Kaushik, K., Verbalyte, M., Sojka, A., Sorci, G., Trenz, H.-J., & EigmĂŒller, M. (2022). Same same but different? Gender politics and (trans-) national value contestation in Europe on Twitter. *Politics and Governance*, *10*(1), 146–160. @@ -2882,7 +2891,7 @@ politics and (trans-) national value contestation in Europe on Twitter.
-312\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in +313\. Wallaschek, S., Starke, C., & BrĂŒning, C. (2020). Solidarity in the public sphere: A discourse network analysis of German newspapers (2008–2017). *Politics and Governance*, *8*(2). @@ -2891,7 +2900,7 @@ the public sphere: A discourse network analysis of German newspapers
-313\. Wang, S. (2018). *Dynamic constructed climate change discourses +314\. Wang, S. (2018). *Dynamic constructed climate change discourses and discourse networks across newspapers in China around three critical policy moments: A comparative study of People’s Daily, China Daily, and Southern Weekend* \[PhD thesis, University of Exeter, Department of @@ -2901,7 +2910,7 @@ Politics\].
-314\. Wang, S. (2024). *Communicating climate change in China. A dynamic +315\. Wang, S. (2024). *Communicating climate change in China. A dynamic discourse approach*. Palgrave Macmillan. @@ -2909,7 +2918,7 @@ discourse approach*. Palgrave Macmillan.
-315\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation +316\. Wang, C., & Wang, L. (2017). Unfolding policies for innovation intermediaries in China: A discourse network analysis. *Science and Public Policy*, *44*(3), 354–368. @@ -2918,7 +2927,7 @@ Public Policy*, *44*(3), 354–368.
-316\. Wang, Y. (2021). Examining the actor coalitions and discourse +317\. Wang, Y. (2021). Examining the actor coalitions and discourse coalitions of the opt-out movement in New York: A discourse network analysis. *Teachers College Record*, *123*(5), 1–26. @@ -2927,7 +2936,7 @@ analysis. *Teachers College Record*, *123*(5), 1–26.
-317\. Wang, Y. (2017). The social networks and paradoxes of the opt-out +318\. Wang, Y. (2017). The social networks and paradoxes of the opt-out movement amid the common core state standards implementation. *Education Policy Analysis Archives*, *25*(34), 1–27. @@ -2936,7 +2945,7 @@ Policy Analysis Archives*, *25*(34), 1–27.
-318\. Wang, Y. (2020). Understanding Congressional coalitions: A +319\. Wang, Y. (2020). Understanding Congressional coalitions: A discourse network analysis of Congressional hearings for the Every Student Succeeds act. *Education Policy Analysis Archives*, *28*(119), 1–30. @@ -2945,7 +2954,7 @@ Student Succeeds act. *Education Policy Analysis Archives*, *28*(119),
-319\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & +320\. Wesche, J. P., Negro, S. O., Brugger, H. I., Eichhammer, W., & Hekkert, M. P. (2024). The influence of visions on cooperation among interest organizations in fragmented socio-technical systems. *Environmental Policy and Governance*, *34*(2), 152–165. @@ -2955,7 +2964,7 @@ interest organizations in fragmented socio-technical systems.
-320\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk +321\. Westenberger, G.-J., & Schneider, V. (2022). Söders Ökofeuerwerk und die grĂŒnfĂ€rbung der CSU: Diskursnetzwerke im bayrischen Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*, *15*(4), 641–665. @@ -2964,7 +2973,7 @@ Themenwettbewerb. *Zeitschrift fĂŒr Vergleichende Politikwissenschaft*,
-321\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The +322\. Wibisono, H., Lovett, J. C., & Anindito, D. B. (2023). The contestation of ideas behind Indonesia’s rural electrification policies: The influence of global and national institutional dynamics. *Development Policy Review*, *41*(1), e12650. @@ -2974,7 +2983,7 @@ The influence of global and national institutional dynamics.
-322\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using +323\. Witkowski, K., Whetsell, T. A., & Ganapati, N. E. (2024). Using collaborative governance to regulate sober living facilities: Structures and strategies for mitigating the influence of powerful actors in multi-sectoral networks. *Administration & Society*, *56*(4), 473–510. @@ -2984,7 +2993,7 @@ multi-sectoral networks. *Administration & Society*, *56*(4), 473–510.
-323\. Wu, J., & Liu, Y. (2020). Deception detection methods +324\. Wu, J., & Liu, Y. (2020). Deception detection methods incorporating discourse network metrics in synchronous computer-mediated communication. *Journal of Information Science*, *46*(1), 64–81. @@ -2993,7 +3002,7 @@ communication. *Journal of Information Science*, *46*(1), 64–81.
-324\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of +325\. Wu, J., & Zhou, L. (2015). DOBNet: Exploiting the discourse of deception behaviour to uncover online deception strategies. *Behaviour & Information Technology*, *34*(9), 936–948. @@ -3002,7 +3011,7 @@ Information Technology*, *34*(9), 936–948.
-325\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission +326\. Yan, K., Wu, H., Bu, K., & Wu, L. (2024). The college admission policy evolution from 2003 to 2020 in China – a social network analysis. *Higher Education Policy*, *37*, 209–236. @@ -3011,7 +3020,7 @@ policy evolution from 2003 to 2020 in China – a social network analysis.
-326\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. +327\. Yap, X.-S., Heiberg, J., Truffer, B., David, E., & Kneib, J.-P. (2023). Emerging global socio-technical regimes for tackling space debris: A discourse network analysis. *Acta Astronautica*, *207*, 445–454. @@ -3020,7 +3029,7 @@ debris: A discourse network analysis. *Acta Astronautica*, *207*,
-327\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring +328\. You, J., Weible, C. M., & Heikkila, T. (2021). Exploring instigator and defender policy scenarios in the siting of energy infrastructure. *Politics & Policy*, *50*(1), 8–32. @@ -3029,7 +3038,7 @@ infrastructure. *Politics & Policy*, *50*(1), 8–32.
-328\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring +329\. Yordy, J., Durnová, A., & Weible, C. M. (2024). Exploring emotional discourses: The case of COVID-19 protests in the US media. *Administrative Theory & Praxis*, *46*(1), 35–54. @@ -3038,7 +3047,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-329\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. +330\. Yordy, J., You, J., Park, K., Weible, C. M., & Heikkila, T. (2019). Framing contests and policy conflicts over gas pipelines. *Review of Policy Research*, *36*(6), 736–756. @@ -3047,7 +3056,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-330\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. +331\. You, J., Yordy, J., Park, K., Heikkila, T., & Weible, C. M. (2020). Policy conflicts in the siting of natural gas pipelines. *Journal of Environmental Policy & Planning*, *22*(4), 501–517. @@ -3056,7 +3065,7 @@ emotional discourses: The case of COVID-19 protests in the US media.
-331\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & +332\. You, J., Yordy, J., Weible, C. M., Park, K., Heikkila, T., & Gilchrist, D. (2021). Comparing policy conflict on electricity transmission line sitings. *Public Policy and Administration*, *38*(1), 107–129. @@ -3065,7 +3074,7 @@ transmission line sitings. *Public Policy and Administration*, *38*(1),
-332\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy +333\. ĆœakovĂĄ, K. (2023). *Expertization of the Czech climate policy network* \[Master’s thesis, Masaryk University, Faculty of Social Studies\]. @@ -3073,7 +3082,7 @@ Studies\].
-333\. Zunaedi, Z. Z. H. (2024). Jejaring wacana cuitan Twitter pada +334\. Zunaedi, Z. Z. H. (2024). Jejaring wacana cuitan Twitter pada kontroversi konten pemeliharaan satwa liar. *Perspektif*, *13*(3), 756–766. diff --git a/dna/build.gradle b/dna/build.gradle index b4d6d65d..77db1c00 100644 --- a/dna/build.gradle +++ b/dna/build.gradle @@ -3,6 +3,10 @@ plugins { id 'java' } +tasks.withType(JavaCompile) { + options.release = 11 +} + // create jar file, not just .class files jar { inputs.dir '.' @@ -28,9 +32,6 @@ jar { } } - sourceCompatibility = '11' - targetCompatibility = '11' - destinationDirectory.set(file("$rootDir/build/")) } @@ -82,3 +83,16 @@ dependencies { // https://mvnrepository.com/artifact/org.apache.commons/commons-math3 implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1' } + +// Extend the default clean task to include custom directories and files +clean { + doFirst { + println "Attempting to delete jar file..." + } + + delete fileTree(dir: "$rootDir/build", include: '**/*.jar') + + doLast { + println "Jar file has been removed." + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a25..9355b415 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +217,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/rDNA/build.gradle b/rDNA/build.gradle index 194ccbdf..86f78796 100644 --- a/rDNA/build.gradle +++ b/rDNA/build.gradle @@ -1,3 +1,11 @@ +import java.nio.file.Files +import java.nio.file.StandardCopyOption + +plugins { + id 'base' +} + +// Task to generate R documentation task rDNADocument { inputs.dir 'rDNA' doLast { @@ -8,55 +16,131 @@ task rDNADocument { } } -task rDNABuild { - dependsOn rDNADocument - inputs.dir 'rDNA' - def outputDir = file("$rootDir/build/") - doFirst { - delete fileTree(dir: outputDir, includes: ['*.tar.gz']) +// Task to find the most recent JAR file in the build directory +def findLatestJarFile() { + def jarDir = file("$rootDir/build") + def jarFiles = jarDir.listFiles({ file -> file.name.endsWith('.jar') } as FileFilter) + + if (jarFiles && jarFiles.size() > 0) { + return jarFiles.sort { -it.lastModified() }[0] + } else { + throw new GradleException("No jar files found in the build directory.") + } +} + +// Task to ensure the inst/java directory exists in the temporary build directory +task createJavaDirInTempBuildDir { + doLast { + def javaDir = file("$rootDir/build/temp-rDNA/inst/java") + if (!javaDir.exists()) { + println "Creating directory: ${javaDir.absolutePath}" + javaDir.mkdirs() + } else { + println "Directory already exists: ${javaDir.absolutePath}" + } + } +} + +// Task to copy the JAR file into the inst/java directory +task copyJarIntoTempBuildDir { + dependsOn ':dna:build', createJavaDirInTempBuildDir + doLast { + def latestJar = findLatestJarFile() + def destinationDir = file("$rootDir/build/temp-rDNA/inst/java") + + println "Copying JAR file: ${latestJar.name} to ${destinationDir.absolutePath}" + + // Copy the JAR file using Java NIO Files class + Files.copy(latestJar.toPath(), new File(destinationDir, latestJar.name).toPath(), StandardCopyOption.REPLACE_EXISTING) + + println "JAR file copied successfully." } +} + +// Task to copy R package sources to a temporary build directory +task copyRPackageSourcesToBuildDir(type: Copy) { + from "$rootDir/rDNA/rDNA" + into "$rootDir/build/temp-rDNA" + exclude 'inst/java/**' +} + +// Task to build the R package (create a .tar.gz file) and clean up after +task rDNABuild(dependsOn: [rDNADocument, copyRPackageSourcesToBuildDir, copyJarIntoTempBuildDir]) { doLast { - mkdir "$rootDir/build" + // Delete old .tar.gz files in the build directory + def buildDir = file("$rootDir/build") + buildDir.listFiles({ file -> file.name.endsWith('.tar.gz') } as FileFilter)?.each { it.delete() } + + // Build the R package in the temporary directory exec { - workingDir "$rootDir/build/" - commandLine 'R', "CMD", "build", "../rDNA/rDNA" + workingDir "$rootDir/build" + commandLine 'R', 'CMD', 'build', 'temp-rDNA' } + + println "R package built successfully." + + // Cleanup the temporary build directory + delete file("$rootDir/build/temp-rDNA") + println "Temporary directory temp-rDNA deleted." } - outputs.dir outputDir } +// Task to check the R package using the .tar.gz file task rDNACheck { - inputs.dir 'rDNA' doLast { - mkdir "$rootDir/build" + def tarGzFile = file("$rootDir/build").listFiles({ file -> file.name.endsWith('.tar.gz') } as FileFilter)[0] exec { - workingDir "rDNA" - commandLine 'R', "-e", "devtools::check(manual = TRUE, check_dir = '../../build/')" + workingDir "$rootDir/build" + commandLine 'R', 'CMD', 'check', '--as-cran', tarGzFile.absolutePath } + println "R package check completed." } - def outputDir = file("$rootDir/build/") - outputs.dir outputDir } -task rDNATest { - inputs.dir 'rDNA' +// Task to test the R package and generate code coverage report +task rDNATest(dependsOn: [rDNADocument, copyRPackageSourcesToBuildDir, copyJarIntoTempBuildDir]) { doLast { - mkdir "$rootDir/build" + def packageName = "rDNA" // Ensure this matches the Package field in DESCRIPTION + def tempDir = file("$rootDir/build/${packageName}") + + // Rename the directory to match the package name + file("$rootDir/build/temp-rDNA").renameTo(tempDir) + + // Run tests using devtools::test() directly on the package source directory exec { - workingDir "rDNA" - commandLine 'R', "-e", "devtools::test()" + workingDir tempDir.absolutePath + commandLine 'R', '-e', 'devtools::test()' } + + // Run code coverage using covr::codecov() on the package source directory exec { - workingDir "rDNA" - commandLine 'R', "-e", "withr::with_envvar(c('NOT_CRAN' = 'true'), covr::codecov(quiet = FALSE))" + workingDir tempDir.absolutePath + commandLine 'R', '-e', 'covr::codecov()' } + + println "R package testing and code coverage completed." + println "Deleting temporary directory..." + delete tempDir + println "Temporary directory deleted." + } +} + +// Extend the default clean task to include custom directories and files +clean { + doFirst { + println "Attempting to delete temporary directory and .tar.gz file..." + } + + delete "$rootDir/build/temp-rDNA" + delete "$rootDir/build/rDNA.Rcheck" + delete fileTree(dir: "$rootDir/build", include: '**/*.tar.gz') + + doLast { + println "Temporary build directories and .tar.gz files have been removed." } } -task build { +// Main build task +build { dependsOn rDNABuild - inputs.dir 'rDNA' - def outputDir = file("$rootDir/build/") - outputs.dir outputDir - doLast {} } diff --git a/rDNA/rDNA/.Rbuildignore b/rDNA/rDNA/.Rbuildignore index d2176e93..91114bf2 100644 --- a/rDNA/rDNA/.Rbuildignore +++ b/rDNA/rDNA/.Rbuildignore @@ -1,3 +1,2 @@ ^.*\.Rproj$ ^\.Rproj\.user$ -inst/java/ diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 33a3623a..30d9889d 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-08-17 +Date: 2024-08-19 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", @@ -21,7 +21,9 @@ Authors@R: role = "ctb")) Maintainer: Philip Leifeld Description: Qualitative content analysis and discourse network analysis - using the Java software Discourse Network Analyzer (DNA). + using the Java software Discourse Network Analyzer (DNA). This + package includes a proprietary Java `.jar` file that is provided for + internal use only. Depends: R (>= 4.1.0) Imports: @@ -44,9 +46,9 @@ Suggests: MCMCpack (>= 1.7-0), coda (>= 0.19-4.1) SystemRequirements: Java (>= 11) -License: GPL-2 URL: https://github.com/leifeld/dna BugReports: https://github.com/leifeld/dna/issues +License: GPL-3 | file LICENSE Encoding: UTF-8 Language: en-US RoxygenNote: 7.3.1 diff --git a/rDNA/rDNA/LICENSE b/rDNA/rDNA/LICENSE new file mode 100644 index 00000000..aee97fa1 --- /dev/null +++ b/rDNA/rDNA/LICENSE @@ -0,0 +1,4 @@ +The R code in this package is licensed under the GPL-3 license. + +The included Java `.jar` file is proprietary and is provided for use within this package only. +No rights are granted to redistribute or modify the `.jar` file. All rights are reserved by the original author. diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 6d3e14dd..c6b04f5d 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -44,7 +44,7 @@ dnaEnvironment <- new.env(hash = TRUE, parent = emptyenv()) #' dna_init() #' } #' -#' @family {rDNA package startup} +#' @family startup #' #' @export #' @importFrom rJava .jinit .jnew .jarray @@ -94,7 +94,7 @@ dna_init <- function(jarfile = dna_jar(), memory = 1024, returnString = FALSE) { #' #' @author Philip Leifeld #' -#' @family {rDNA package startup} +#' @family startup #' #' @export dna_getHeadlessDna <- function() { @@ -108,18 +108,18 @@ dna_getHeadlessDna <- function() { #' rDNA requires the installation of a DNA jar file to run properly. While it is #' possible to store the jar file in the respective working directory, it is #' preferable to install it in the rDNA library installation directory under -#' \code{inst/java/}. The \code{dna_jar} function attempts to find the version -#' of the jar file that matches the installed \pkg{rDNA} version in the -#' \code{inst/java/} sub-directory of the package library path and return the -#' jar file name including its full path. If this fails, it will try to find the -#' jar file in the current working directory and return its file name. If this -#' fails as well, it will attempt to download the matching jar file from GitHub -#' and store it in the library path and return its file name. If this fails, it -#' will attempt to store the downloaded jar file in the working directory and -#' return its file name. If this fails as well, it will clone the current DNA -#' master code from GitHub to a local temporary directory, build the jar file -#' from source, and attempt to store the built jar file in the library path or, -#' if this fails, in the working directory and return the file name of the jar +#' \code{java/}. The \code{dna_jar} function attempts to find the version of the +#' jar file that matches the installed \pkg{rDNA} version in the \code{java/} +#' sub-directory of the package library path and return the jar file name +#' including its full path. If this fails, it will try to find the jar file in +#' the current working directory and return its file name. If this fails as +#' well, it will attempt to download the matching jar file from GitHub and store +#' it in the library path and return its file name. If this fails, it will +#' attempt to store the downloaded jar file in the working directory and return +#' its file name. If this fails as well, it will clone the current DNA master +#' code from GitHub to a local temporary directory, build the jar file from +#' source, and attempt to store the built jar file in the library path or, if +#' this fails, in the working directory and return the file name of the jar #' file. If all of this fails, an error message is thrown. #' #' @return The file name of the jar file that matches the installed \pkg{rDNA} @@ -127,7 +127,7 @@ dna_getHeadlessDna <- function() { #' #' @author Philip Leifeld #' -#' @family {rDNA package startup} +#' @family startup #' #' @importFrom utils download.file unzip packageVersion #' @export @@ -135,7 +135,17 @@ dna_jar <- function() { # detect package version v <- as.character(packageVersion("rDNA")) - # try to locate jar file in library path and return jar file path + # try to locate jar file in library path under java/ and return jar file path + tryCatch({ + rdna_dir <- dirname(system.file(".", package = "rDNA")) + jar <- paste0(rdna_dir, "/java/dna-", v, ".jar") + if (file.exists(jar)) { + message("Jar file found in library path.") + return(jar) + } + }, error = function(e) {success <- FALSE}) + + # try to locate jar file in library path under inst/java/ and return jar file path tryCatch({ rdna_dir <- dirname(system.file(".", package = "rDNA")) jar <- paste0(rdna_dir, "/inst/java/dna-", v, ".jar") @@ -147,7 +157,7 @@ dna_jar <- function() { # try to locate jar file in working directory and return jar file path tryCatch({ - jar <- paste0(getwd(), "/inst/java/dna-", v, ".jar") + jar <- paste0(getwd(), "/dna-", v, ".jar") if (file.exists(jar)) { message("Jar file found in working directory.") return(jar) @@ -158,8 +168,8 @@ dna_jar <- function() { tryCatch({ rdna_dir <- dirname(system.file(".", package = "rDNA")) f <- paste0("https://github.com/leifeld/dna/releases/download/v", v, "/dna-", v, ".jar") - dest <- paste0(rdna_dir, "/inst/java/dna-", v, ".jar") - targetdir <- paste0(rdna_dir, "/", "inst/java/") + dest <- paste0(rdna_dir, "/java/dna-", v, ".jar") + targetdir <- paste0(rdna_dir, "/java/") dir.create(targetdir, recursive = TRUE, showWarnings = FALSE) suppressWarnings(download.file(url = f, destfile = dest, @@ -213,7 +223,7 @@ dna_jar <- function() { # try to copy built jar to library path tryCatch({ - targetdir <- paste0(find.package("rDNA"), "/", "inst/java/") + targetdir <- paste0(find.package("rDNA"), "/java/") dir.create(targetdir, recursive = TRUE, showWarnings = FALSE) dest <- paste0(targetdir, "dna-", v, ".jar") file.copy(from = builtjar, to = targetdir) @@ -237,7 +247,7 @@ dna_jar <- function() { stop("DNA jar file could not be identified or downloaded. Please download ", "the DNA jar file matching the version number of rDNA and store it in ", - "the inst/java/ directory of your rDNA library installation path or in ", + "the java/ sub-directory of your rDNA library installation path or in ", "your working directory. Your current rDNA version is ", v, ".") } @@ -260,7 +270,7 @@ dna_jar <- function() { #' #' @author Johannes B. Gruber, Philip Leifeld #' -#' @family {rDNA package startup} +#' @family startup #' #' @export dna_sample <- function(overwrite = FALSE) { @@ -313,7 +323,7 @@ dna_sample <- function(overwrite = FALSE) { #' #' @author Philip Leifeld #' -#' @family {rDNA database connections} +#' @family database #' @seealso \code{\link{dna_queryCoders}} #' #' @examples @@ -405,7 +415,7 @@ dna_openDatabase <- function(db_url, #' dna_closeDatabase() #' } #' -#' @family {rDNA database connections} +#' @family database #' #' @export #' @importFrom rJava .jcall @@ -450,7 +460,7 @@ dna_closeDatabase <- function() { #' dna_openConnectionProfile(file = "my profile.dnc", coderPassword = "sample") #' } #' -#' @family {rDNA database connections} +#' @family database #' #' @export #' @importFrom rJava .jcall @@ -505,7 +515,7 @@ dna_openConnectionProfile <- function(file, coderPassword = "") { #' #' @author Philip Leifeld #' -#' @family {rDNA database connections} +#' @family database #' #' @examples #' \dontrun{ @@ -560,7 +570,7 @@ dna_saveConnectionProfile <- function(file, coderPassword = "") { #' dna_printDetails() #' } #' -#' @family {rDNA database connections} +#' @family database #' #' @export #' @importFrom rJava .jcall @@ -603,7 +613,7 @@ dna_printDetails <- function() { #' } #' } #' -#' @family {rDNA database connections} +#' @family database #' #' @export dna_api <- function() { @@ -779,7 +789,7 @@ dna_getVariables <- function(statementType) { #' #' @author Philip Leifeld #' -#' @family {rDNA attributes} +#' @family attributes #' #' @importFrom rJava .jcall #' @importFrom rJava J @@ -1098,7 +1108,7 @@ dna_getAttributes <- function(statementType = NULL, #' #' @author Philip Leifeld #' -#' @family {rDNA networks} +#' @family networks #' #' @importFrom rJava .jarray #' @importFrom rJava .jcall @@ -1291,7 +1301,7 @@ dna_network <- function(networkType = "twomode", #' #' @author Philip Leifeld #' -#' @family {rDNA networks} +#' @family networks #' #' @export as.matrix.dna_network_onemode <- function(x, ...) { @@ -1316,7 +1326,7 @@ as.matrix.dna_network_onemode <- function(x, ...) { #' #' @author Philip Leifeld #' -#' @family {rDNA networks} +#' @family networks #' #' @export as.matrix.dna_network_twomode <- as.matrix.dna_network_onemode @@ -1340,7 +1350,7 @@ as.matrix.dna_network_twomode <- as.matrix.dna_network_onemode #' #' @author Philip Leifeld #' -#' @family {rDNA networks} +#' @family networks #' #' @export print.dna_network_onemode <- function(x, trim = 5, attr = TRUE, ...) { @@ -1393,7 +1403,7 @@ print.dna_network_onemode <- function(x, trim = 5, attr = TRUE, ...) { #' #' @author Philip Leifeld #' -#' @family {rDNA networks} +#' @family networks #' #' @export print.dna_network_twomode <- print.dna_network_onemode @@ -1639,7 +1649,7 @@ print.dna_network_twomode <- print.dna_network_onemode #' #' @author Tim Henrichsen #' -#' @family {rDNA networks} +#' @family networks #' #' @importFrom ggplot2 autoplot #' @importFrom ggplot2 aes @@ -1977,7 +1987,7 @@ autoplot.dna_network_twomode <- autoplot.dna_network_onemode #' } #' #' @author Philip Leifeld -#' @family {rDNA networks} +#' @family networks #' @importFrom rlang .data #' @export dna_tidygraph <- function(network, attributes = NULL, ...) { @@ -4753,8 +4763,8 @@ dna_phaseTransitions <- function(distanceMethod = "absdiff", timeWindow = "days", windowSize = 200, kernel = "uniform", - normalizeToOne = FALSE, - indentTime = TRUE, + normalizeToOne = TRUE, + indentTime = FALSE, excludeValues = list(), excludeAuthors = character(), excludeSources = character(), diff --git a/rDNA/rDNA/inst/CITATION b/rDNA/rDNA/inst/CITATION index 4141a39f..760d4a6e 100644 --- a/rDNA/rDNA/inst/CITATION +++ b/rDNA/rDNA/inst/CITATION @@ -1,18 +1,18 @@ -citHeader("To cite package rDNA in publications use:") - -if(!exists("meta") || is.null(meta)) meta <- packageDescription("rDNA") -year <- sub(".*(2[[:digit:]]{3})-.*", "\\1", meta$Date) -vers <- paste("R package version", meta$Version) -author <- meta$Author - -citEntry(entry="Manual", - title = "{rDNA. Discourse Network Analysis in R}", - author = author, - year = year, - note = vers, - organization = paste("University of Essex, Department of Government"), - address = "Colchester, UK", - textVersion = paste0("Leifeld, Philip (", year, "). rDNA. Discourse ", - "Network Analysis in R. ", vers, "."), - url = "https://github.com/leifeld/dna" +citEntry <- bibentry( + bibtype = "incollection", + title = "Discourse Network Analysis: Policy Debates as Dynamic Networks", + author = person(c("Philip"), "Leifeld"), + booktitle = "The Oxford Handbook of Political Networks", + editor = c( + person(c("Jennifer N."), "Victor"), + person(c("Alexander H."), "Montgomery"), + person(c("Mark N."), "Lubell") + ), + publisher = "Oxford University Press", + year = "2017", + chapter = "12", + pages = "301--325" ) + +citHeader("To cite this package, please use the following reference:") +citEntry diff --git a/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd b/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd index f447298d..0c5abb89 100644 --- a/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd +++ b/rDNA/rDNA/man/as.matrix.dna_network_onemode.Rd @@ -20,7 +20,7 @@ Remove the attributes and \code{"dna_network_onemode"} class label from a \code{dna_network_onemode} object and return it as a numeric matrix. } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, @@ -31,4 +31,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd b/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd index 1c15deb4..ab5aded5 100644 --- a/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd +++ b/rDNA/rDNA/man/as.matrix.dna_network_twomode.Rd @@ -20,7 +20,7 @@ Remove the attributes and \code{"dna_network_twomode"} class label from a \code{dna_network_twomode} object and return it as a numeric matrix. } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{autoplot.dna_network}}, \code{\link{dna_network}()}, @@ -31,4 +31,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/autoplot.dna_network.Rd b/rDNA/rDNA/man/autoplot.dna_network.Rd index 408bc862..08141092 100644 --- a/rDNA/rDNA/man/autoplot.dna_network.Rd +++ b/rDNA/rDNA/man/autoplot.dna_network.Rd @@ -304,7 +304,7 @@ autoplot(nw, atts = atts, node_colors = "color") + } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{dna_network}()}, @@ -315,4 +315,4 @@ Other {rDNA networks}: \author{ Tim Henrichsen } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/dna_api.Rd b/rDNA/rDNA/man/dna_api.Rd index 26830bf1..b6758a60 100644 --- a/rDNA/rDNA/man/dna_api.Rd +++ b/rDNA/rDNA/man/dna_api.Rd @@ -42,7 +42,7 @@ for (i in seq(variable_references$size()) - 1) { } \seealso{ -Other {rDNA database connections}: +Other database: \code{\link{dna_closeDatabase}()}, \code{\link{dna_openConnectionProfile}()}, \code{\link{dna_openDatabase}()}, @@ -52,4 +52,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_closeDatabase.Rd b/rDNA/rDNA/man/dna_closeDatabase.Rd index a9488a7c..f25edae1 100644 --- a/rDNA/rDNA/man/dna_closeDatabase.Rd +++ b/rDNA/rDNA/man/dna_closeDatabase.Rd @@ -25,7 +25,7 @@ dna_closeDatabase() } \seealso{ -Other {rDNA database connections}: +Other database: \code{\link{dna_api}()}, \code{\link{dna_openConnectionProfile}()}, \code{\link{dna_openDatabase}()}, @@ -35,4 +35,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_getAttributes.Rd b/rDNA/rDNA/man/dna_getAttributes.Rd index e572b781..fb2dc6e2 100644 --- a/rDNA/rDNA/man/dna_getAttributes.Rd +++ b/rDNA/rDNA/man/dna_getAttributes.Rd @@ -60,4 +60,4 @@ dna_getAttributes(statementType = "DNA Statement", variable = "concept") \author{ Philip Leifeld } -\concept{{rDNA attributes}} +\concept{attributes} diff --git a/rDNA/rDNA/man/dna_getHeadlessDna.Rd b/rDNA/rDNA/man/dna_getHeadlessDna.Rd index dce52f07..6548d7fd 100644 --- a/rDNA/rDNA/man/dna_getHeadlessDna.Rd +++ b/rDNA/rDNA/man/dna_getHeadlessDna.Rd @@ -27,7 +27,7 @@ auto-complete function access after typing the "$" if the headless reference has been saved to an object in the workspace. } \seealso{ -Other {rDNA package startup}: +Other startup: \code{\link{dna_init}()}, \code{\link{dna_jar}()}, \code{\link{dna_sample}()} @@ -35,4 +35,4 @@ Other {rDNA package startup}: \author{ Philip Leifeld } -\concept{{rDNA package startup}} +\concept{startup} diff --git a/rDNA/rDNA/man/dna_init.Rd b/rDNA/rDNA/man/dna_init.Rd index c72d8c6f..0023fc3c 100644 --- a/rDNA/rDNA/man/dna_init.Rd +++ b/rDNA/rDNA/man/dna_init.Rd @@ -35,7 +35,7 @@ dna_init() } \seealso{ -Other {rDNA package startup}: +Other startup: \code{\link{dna_getHeadlessDna}()}, \code{\link{dna_jar}()}, \code{\link{dna_sample}()} @@ -43,4 +43,4 @@ Other {rDNA package startup}: \author{ Philip Leifeld } -\concept{{rDNA package startup}} +\concept{startup} diff --git a/rDNA/rDNA/man/dna_jar.Rd b/rDNA/rDNA/man/dna_jar.Rd index 82e4af72..cfd8f3d5 100644 --- a/rDNA/rDNA/man/dna_jar.Rd +++ b/rDNA/rDNA/man/dna_jar.Rd @@ -17,22 +17,22 @@ Identify and/or download and install the correct DNA jar file. rDNA requires the installation of a DNA jar file to run properly. While it is possible to store the jar file in the respective working directory, it is preferable to install it in the rDNA library installation directory under -\code{inst/java/}. The \code{dna_jar} function attempts to find the version -of the jar file that matches the installed \pkg{rDNA} version in the -\code{inst/java/} sub-directory of the package library path and return the -jar file name including its full path. If this fails, it will try to find the -jar file in the current working directory and return its file name. If this -fails as well, it will attempt to download the matching jar file from GitHub -and store it in the library path and return its file name. If this fails, it -will attempt to store the downloaded jar file in the working directory and -return its file name. If this fails as well, it will clone the current DNA -master code from GitHub to a local temporary directory, build the jar file -from source, and attempt to store the built jar file in the library path or, -if this fails, in the working directory and return the file name of the jar +\code{java/}. The \code{dna_jar} function attempts to find the version of the +jar file that matches the installed \pkg{rDNA} version in the \code{java/} +sub-directory of the package library path and return the jar file name +including its full path. If this fails, it will try to find the jar file in +the current working directory and return its file name. If this fails as +well, it will attempt to download the matching jar file from GitHub and store +it in the library path and return its file name. If this fails, it will +attempt to store the downloaded jar file in the working directory and return +its file name. If this fails as well, it will clone the current DNA master +code from GitHub to a local temporary directory, build the jar file from +source, and attempt to store the built jar file in the library path or, if +this fails, in the working directory and return the file name of the jar file. If all of this fails, an error message is thrown. } \seealso{ -Other {rDNA package startup}: +Other startup: \code{\link{dna_getHeadlessDna}()}, \code{\link{dna_init}()}, \code{\link{dna_sample}()} @@ -40,4 +40,4 @@ Other {rDNA package startup}: \author{ Philip Leifeld } -\concept{{rDNA package startup}} +\concept{startup} diff --git a/rDNA/rDNA/man/dna_network.Rd b/rDNA/rDNA/man/dna_network.Rd index 81f3c20b..e44579fd 100644 --- a/rDNA/rDNA/man/dna_network.Rd +++ b/rDNA/rDNA/man/dna_network.Rd @@ -287,7 +287,7 @@ nw <- dna_network(networkType = "onemode", } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, @@ -298,4 +298,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/dna_openConnectionProfile.Rd b/rDNA/rDNA/man/dna_openConnectionProfile.Rd index 8cd02116..0c19276b 100644 --- a/rDNA/rDNA/man/dna_openConnectionProfile.Rd +++ b/rDNA/rDNA/man/dna_openConnectionProfile.Rd @@ -45,7 +45,7 @@ dna_openConnectionProfile(file = "my profile.dnc", coderPassword = "sample") } \seealso{ -Other {rDNA database connections}: +Other database: \code{\link{dna_api}()}, \code{\link{dna_closeDatabase}()}, \code{\link{dna_openDatabase}()}, @@ -55,4 +55,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_openDatabase.Rd b/rDNA/rDNA/man/dna_openDatabase.Rd index a235d245..63117103 100644 --- a/rDNA/rDNA/man/dna_openDatabase.Rd +++ b/rDNA/rDNA/man/dna_openDatabase.Rd @@ -69,7 +69,7 @@ dna_openDatabase(coderId = 1, \seealso{ \code{\link{dna_queryCoders}} -Other {rDNA database connections}: +Other database: \code{\link{dna_api}()}, \code{\link{dna_closeDatabase}()}, \code{\link{dna_openConnectionProfile}()}, @@ -79,4 +79,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_phaseTransitions.Rd b/rDNA/rDNA/man/dna_phaseTransitions.Rd index e9286dc2..3c8d1561 100644 --- a/rDNA/rDNA/man/dna_phaseTransitions.Rd +++ b/rDNA/rDNA/man/dna_phaseTransitions.Rd @@ -31,8 +31,8 @@ dna_phaseTransitions( timeWindow = "days", windowSize = 200, kernel = "uniform", - normalizeToOne = FALSE, - indentTime = TRUE, + normalizeToOne = TRUE, + indentTime = FALSE, excludeValues = list(), excludeAuthors = character(), excludeSources = character(), diff --git a/rDNA/rDNA/man/dna_printDetails.Rd b/rDNA/rDNA/man/dna_printDetails.Rd index a2021f9e..bcfcb6af 100644 --- a/rDNA/rDNA/man/dna_printDetails.Rd +++ b/rDNA/rDNA/man/dna_printDetails.Rd @@ -26,7 +26,7 @@ dna_printDetails() } \seealso{ -Other {rDNA database connections}: +Other database: \code{\link{dna_api}()}, \code{\link{dna_closeDatabase}()}, \code{\link{dna_openConnectionProfile}()}, @@ -36,4 +36,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_sample.Rd b/rDNA/rDNA/man/dna_sample.Rd index 20e3a0e3..ad53f4f2 100644 --- a/rDNA/rDNA/man/dna_sample.Rd +++ b/rDNA/rDNA/man/dna_sample.Rd @@ -26,7 +26,7 @@ dna_openDatabase(s) } \seealso{ -Other {rDNA package startup}: +Other startup: \code{\link{dna_getHeadlessDna}()}, \code{\link{dna_init}()}, \code{\link{dna_jar}()} @@ -34,4 +34,4 @@ Other {rDNA package startup}: \author{ Johannes B. Gruber, Philip Leifeld } -\concept{{rDNA package startup}} +\concept{startup} diff --git a/rDNA/rDNA/man/dna_saveConnectionProfile.Rd b/rDNA/rDNA/man/dna_saveConnectionProfile.Rd index 88b0aec7..5a98eb40 100644 --- a/rDNA/rDNA/man/dna_saveConnectionProfile.Rd +++ b/rDNA/rDNA/man/dna_saveConnectionProfile.Rd @@ -43,7 +43,7 @@ dna_saveConnectionProfile(file = "my profile.dnc", coderPassword = "sample") } \seealso{ -Other {rDNA database connections}: +Other database: \code{\link{dna_api}()}, \code{\link{dna_closeDatabase}()}, \code{\link{dna_openConnectionProfile}()}, @@ -53,4 +53,4 @@ Other {rDNA database connections}: \author{ Philip Leifeld } -\concept{{rDNA database connections}} +\concept{database} diff --git a/rDNA/rDNA/man/dna_tidygraph.Rd b/rDNA/rDNA/man/dna_tidygraph.Rd index e695a2ac..1b67677a 100644 --- a/rDNA/rDNA/man/dna_tidygraph.Rd +++ b/rDNA/rDNA/man/dna_tidygraph.Rd @@ -136,7 +136,7 @@ intergraph::asNetwork(g) } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, @@ -147,4 +147,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/print.dna_network_onemode.Rd b/rDNA/rDNA/man/print.dna_network_onemode.Rd index 609f353e..b243ff63 100644 --- a/rDNA/rDNA/man/print.dna_network_onemode.Rd +++ b/rDNA/rDNA/man/print.dna_network_onemode.Rd @@ -28,7 +28,7 @@ Show details of a \code{dna_network_onemode} object. Print a one-mode network matrix and its attributes. } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, @@ -39,4 +39,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/man/print.dna_network_twomode.Rd b/rDNA/rDNA/man/print.dna_network_twomode.Rd index b611cb67..6ddbdb57 100644 --- a/rDNA/rDNA/man/print.dna_network_twomode.Rd +++ b/rDNA/rDNA/man/print.dna_network_twomode.Rd @@ -28,7 +28,7 @@ Show details of a \code{dna_network_twomode} object. Print a two-mode network matrix and its attributes. } \seealso{ -Other {rDNA networks}: +Other networks: \code{\link{as.matrix.dna_network_onemode}()}, \code{\link{as.matrix.dna_network_twomode}()}, \code{\link{autoplot.dna_network}}, @@ -39,4 +39,4 @@ Other {rDNA networks}: \author{ Philip Leifeld } -\concept{{rDNA networks}} +\concept{networks} diff --git a/rDNA/rDNA/tests/testthat/test-clustering.R b/rDNA/rDNA/tests/testthat/test-clustering.R index ad1578f2..894af7eb 100644 --- a/rDNA/rDNA/tests/testthat/test-clustering.R +++ b/rDNA/rDNA/tests/testthat/test-clustering.R @@ -8,7 +8,7 @@ test_that("Example 1 produces expected output", { skip_if_not_installed("sna", minimum_version = "2.4") skip_if_not_installed("cluster", minimum_version = "1.12.0") samp <- dna_sample() - dna_init(samp) + dna_init() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") mc1 <- dna_multiclust(variable1 = "organization", variable2 = "concept", @@ -17,7 +17,7 @@ test_that("Example 1 produces expected output", { k = 0, saveObjects = TRUE) expect_s3_class(mc1, "dna_multiclust") - expect_named(mc1, c("modularity", "max_mod", "memberships", "cl")) + expect_equal(names(mc1), c("cl", "k", "max_mod", "memberships", "modularity")) expect_true(length(mc1$modularity) > 0) expect_true(length(mc1$max_mod) > 0) expect_true(length(mc1$memberships) > 0) @@ -32,7 +32,7 @@ test_that("Example 2 produces expected output", { testthat::skip_on_ci() skip_if_not_installed("igraph", minimum_version = "0.8.1") samp <- dna_sample() - dna_init(samp) + dna_init() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") set.seed(12345) mc2 <- dna_multiclust(k = 2, @@ -54,7 +54,7 @@ test_that("Example 2 produces expected output", { label_prop = FALSE, spinglass = FALSE) expect_s3_class(mc2, "dna_multiclust") - expect_named(mc2, c("modularity", "memberships")) + expect_equal(names(mc2), c("k", "max_mod", "memberships", "modularity")) expect_true(length(mc2$modularity) > 0) expect_true(length(mc2$memberships) > 0) dna_closeDatabase() @@ -69,13 +69,13 @@ test_that("Example 3 produces expected output", { skip_if_not_installed("sna", minimum_version = "2.4") skip_if_not_installed("cluster", minimum_version = "1.12.0") samp <- dna_sample() - dna_init(samp) + dna_init() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") mc3 <- dna_multiclust(k = 2, timeWindow = "events", windowSize = 28) expect_s3_class(mc3, "dna_multiclust") - expect_named(mc3, c("max_mod")) + expect_equal(names(mc3), c("k", "max_mod", "memberships", "modularity")) expect_true(length(mc3$max_mod) > 0) dna_closeDatabase() unlink(samp) diff --git a/rDNA/rDNA/tests/testthat/test-phasetransitions.R b/rDNA/rDNA/tests/testthat/test-phasetransitions.R index 866ef9e9..24eea179 100644 --- a/rDNA/rDNA/tests/testthat/test-phasetransitions.R +++ b/rDNA/rDNA/tests/testthat/test-phasetransitions.R @@ -3,7 +3,7 @@ context("Testing phase transitions") # Create a function to set up the database for tests setup_dna_database <- function() { dna_init() - samp <- dna_sample() + samp <- dna_sample(overwrite = TRUE) dna_openDatabase(samp, coderId = 1, coderPassword = "sample") return(samp) } @@ -14,31 +14,38 @@ cleanup_dna_database <- function(samp) { unlink(samp) } -test_that("dna_phaseTransitions produces expected output with default settings", { +test_that("dna_phaseTransitions produces expected output with help file example", { testthat::skip_on_cran() testthat::skip_on_ci() samp <- setup_dna_database() - result <- dna_phaseTransitions() - - expect_type(result, "dna_phaseTransitions") - expect_true(!is.null(result$states)) - expect_true(!is.null(result$modularity)) - expect_true(!is.null(result$clusterMethod)) - expect_true(!is.null(result$distmat)) - - cleanup_dna_database(samp) -}) - -test_that("autoplot.dna_phaseTransitions produces expected plots", { - testthat::skip_on_cran() - testthat::skip_on_ci() - samp <- setup_dna_database() - phase_trans_obj <- dna_phaseTransitions() - - plots <- autoplot.dna_phaseTransitions(phase_trans_obj) + results <- dna_phaseTransitions(distanceMethod = "spectral", + clusterMethods = c("ward", + "pam", + "concor", + "walktrap"), + k.min = 2, + k.max = 6, + networkType = "onemode", + variable1 = "organization", + variable2 = "concept", + timeWindow = "days", + windowSize = 15, + kernel = "gaussian", + indentTime = FALSE, + normalizeToOne = FALSE) + expect_true("dna_phaseTransitions" %in% class(results)) + expect_true(!is.null(results$states)) + expect_true(!is.null(results$modularity)) + expect_true(!is.null(results$clusterMethod)) + expect_true(!is.null(results$distmat)) + + plots <- autoplot(results) expect_type(plots, "list") expect_length(plots, 4) - expect_type(plots[[1]], "ggplot") + expect_true("egg" %in% class(plots[[1]])) + expect_true("ggplot" %in% class(plots[[2]])) + expect_true("ggplot" %in% class(plots[[3]])) + expect_true("ggplot" %in% class(plots[[4]])) cleanup_dna_database(samp) }) diff --git a/rDNA/rDNA/tests/testthat/test-scaling.R b/rDNA/rDNA/tests/testthat/test-scaling.R index 8ee50e03..2d60d8c7 100644 --- a/rDNA/rDNA/tests/testthat/test-scaling.R +++ b/rDNA/rDNA/tests/testthat/test-scaling.R @@ -3,7 +3,7 @@ context("Testing IRT scaling") # Create a function to set up the database for tests setup_dna_database <- function() { dna_init() - samp <- dna_sample() + samp <- dna_sample(overwrite = TRUE) dna_openDatabase(samp, coderId = 1, coderPassword = "sample") return(samp) } @@ -51,7 +51,7 @@ test_that("dna_scale1dbin produces expected output with help file example", { expect_equal(dim(fit_1d_bin$ability), c(7, 9)) expect_equal(dim(fit_1d_bin$discrimination), c(5, 8)) expect_equal(dim(fit_1d_bin$difficulty), c(5, 8)) - expect_length(utils::capture.output(fit_1d_bin), 27) + expect_true(length(utils::capture.output(fit_1d_bin)) > 1) plot_1d_bin <- autoplot(fit_1d_bin) expect_length(plot_1d_bin, 9) classes <- unique(sapply(plot_1d_bin, class)[2, ]) @@ -93,7 +93,7 @@ test_that("dna_scale2dbin produces expected output with help file example", { expect_equal(names(fit_2d_bin), c("sample", "ability", "call")) expect_equal(dim(fit_2d_bin$sample), c(2000, 14)) expect_equal(dim(fit_2d_bin$ability), c(7, 12)) - expect_length(utils::capture.output(fit_2d_bin), 11) + expect_true(length(utils::capture.output(fit_2d_bin)) > 1) plot_2d_bin <- autoplot(fit_2d_bin) expect_length(plot_2d_bin, 3) classes <- unique(sapply(plot_2d_bin, class)[2, ]) @@ -135,7 +135,7 @@ test_that("dna_scale1dord produces expected output with help file example", { expect_equal(names(fit_1d_ord), c("sample", "ability", "call")) expect_equal(dim(fit_1d_ord$sample), c(2000, 7)) expect_equal(dim(fit_1d_ord$ability), c(7, 9)) - expect_length(utils::capture.output(fit_1d_ord), 11) + expect_true(length(utils::capture.output(fit_1d_ord)) > 1) plot_1d_ord <- autoplot(fit_1d_ord) expect_length(plot_1d_ord, 3) classes <- unique(sapply(plot_1d_ord, class)[2, ]) @@ -181,7 +181,7 @@ test_that("dna_scale2dord produces expected output with help file example", { expect_equal(dim(fit_2d_ord$ability), c(7, 12)) expect_equal(dim(fit_2d_ord$discrimination), c(5, 11)) expect_equal(dim(fit_2d_ord$difficulty), c(5, 8)) - expect_length(utils::capture.output(fit_2d_ord), 27) + expect_true(length(utils::capture.output(fit_2d_ord)) > 1) plot_2d_ord <- autoplot(fit_2d_ord) expect_length(plot_2d_ord, 9) classes <- unique(sapply(plot_2d_ord, class)[2, ]) diff --git a/rDNA/rDNA/tests/testthat/test-zzz.R b/rDNA/rDNA/tests/testthat/test-zzz.R index 68f66f72..0094cefc 100644 --- a/rDNA/rDNA/tests/testthat/test-zzz.R +++ b/rDNA/rDNA/tests/testthat/test-zzz.R @@ -4,5 +4,5 @@ teardown({ unlink("sample.dna") unlink("profile.dnc") unlink("test.dna") - unlink("inst/java/*.jar") + unlink("java/*.jar") }) \ No newline at end of file From a7a2b88585a7e3186bc10569dec3bd7664b29d92 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:01:37 +0200 Subject: [PATCH 24/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index fd136281..71ceaa3a 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -44,6 +44,7 @@ jobs: extra-packages: | any::devtools any::testthat + any::roxygen2 any::ggplot2 any::roxygen2 any::igraph @@ -67,10 +68,13 @@ jobs: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-tinytex@v2 - uses: gradle/wrapper-validation-action@v1 + - name: Check Pandoc Installation + run: | + pandoc --version - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 - name: Execute Gradle build for bibliography - run: ./gradlew :bibliography:build + run: ./gradlew :bibliography:build --info --stacktrace - name: Store artifacts uses: actions/upload-artifact@v3 with: From d675bd7d70f814885827dbe9953b150058b19195 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:31:30 +0200 Subject: [PATCH 25/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index 71ceaa3a..d433b18e 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -68,6 +68,15 @@ jobs: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-tinytex@v2 - uses: gradle/wrapper-validation-action@v1 + - name: Restore System Dependencies + uses: actions/cache@v3 + with: + path: | + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + /usr/local/texlive/tlpkg/texlive.tlpdb + key: ${{ runner.os }}-system-deps-v1 - name: Check Pandoc Installation run: | pandoc --version @@ -110,6 +119,20 @@ jobs: steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 + - name: Restore System Dependencies + uses: actions/cache@v3 + with: + path: | + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + /usr/local/texlive/tlpkg/texlive.tlpdb + key: ${{ runner.os }}-system-deps-v1 + - name: Restore R Packages + uses: actions/cache@v3 + with: + path: ~/.cache/R + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 @@ -144,6 +167,20 @@ jobs: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true + - name: Restore System Dependencies + uses: actions/cache@v3 + with: + path: | + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + /usr/local/texlive/tlpkg/texlive.tlpdb + key: ${{ runner.os }}-system-deps-v1 + - name: Restore R Packages + uses: actions/cache@v3 + with: + path: ~/.cache/R + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck @@ -160,6 +197,20 @@ jobs: steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 + - name: Restore System Dependencies + uses: actions/cache@v3 + with: + path: | + /usr/local/texlive + /usr/local/bin/pandoc + /usr/local/texlive/texmf-var + /usr/local/texlive/tlpkg/texlive.tlpdb + key: ${{ runner.os }}-system-deps-v1 + - name: Restore R Packages + uses: actions/cache@v3 + with: + path: ~/.cache/R + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 From 41cb84521606593edb456123f739ca2bb8624651 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:34:51 +0200 Subject: [PATCH 26/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index d433b18e..7b405619 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -4,7 +4,20 @@ on: [push, pull_request, workflow_dispatch] jobs: install-dependencies: - runs-on: ubuntu-latest + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + strategy: + fail-fast: false + matrix: + config: + - {os: macOS-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes steps: - uses: actions/checkout@v3 From f1764f26435d9e143280fc1a01002dd2196f52da Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:45:55 +0200 Subject: [PATCH 27/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 49 ++++++++++++++++----------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index 7b405619..d73d1b29 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -3,21 +3,8 @@ name: build on: [push, pull_request, workflow_dispatch] jobs: - install-dependencies: - runs-on: ${{ matrix.config.os }} - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - strategy: - fail-fast: false - matrix: - config: - - {os: macOS-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes + install-system-dependencies: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -38,6 +25,23 @@ jobs: sudo apt-get update sudo apt-get install -y texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-bibtex-extra pandoc pandoc-citeproc + install-r-dependencies: + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + strategy: + fail-fast: false + matrix: + config: + - {os: macOS-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + steps: + - uses: actions/checkout@v3 - uses: actions/cache@v3 id: r-packages-cache with: @@ -75,7 +79,7 @@ jobs: any::pbmcapply Bibliography: - needs: install-dependencies + needs: install-system-dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -127,7 +131,7 @@ jobs: path: build/*.jar rDNA: - needs: [DNA, install-dependencies] + needs: [DNA, install-system-dependencies, install-r-dependencies] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -159,7 +163,7 @@ jobs: rDNA-check: runs-on: ${{ matrix.config.os }} - needs: rDNA + needs: [install-system-dependencies, install-r-dependencies] name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false @@ -194,6 +198,11 @@ jobs: with: path: ~/.cache/R key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + - uses: gradle/wrapper-validation-action@v1 + - name: Setup Gradle + uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle build for rDNA + run: ./gradlew :rDNA:build - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck @@ -206,7 +215,7 @@ jobs: rDNA-test: runs-on: ubuntu-latest - needs: [rDNA, install-dependencies] + needs: [install-system-dependencies, install-r-dependencies] steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 @@ -227,6 +236,8 @@ jobs: - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle build for rDNA + run: ./gradlew :rDNA:build - name: Execute Gradle rDNATest Task run: ./gradlew :rDNA:rDNATest - name: Store test results From 06eb5394c4c11a69e08b464dfeac8d2ccb2ed6c5 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:47:24 +0200 Subject: [PATCH 28/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index d73d1b29..f184fae4 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -163,7 +163,7 @@ jobs: rDNA-check: runs-on: ${{ matrix.config.os }} - needs: [install-system-dependencies, install-r-dependencies] + needs: [DNA, install-system-dependencies, install-r-dependencies] name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false @@ -215,7 +215,7 @@ jobs: rDNA-test: runs-on: ubuntu-latest - needs: [install-system-dependencies, install-r-dependencies] + needs: [DNA, install-system-dependencies, install-r-dependencies] steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 From 73c4de979682f6c3908b9eab1e411bd43411734f Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 19 Aug 2024 18:50:21 +0200 Subject: [PATCH 29/43] Updated GitHub actions --- .github/workflows/build-and-check.yml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-and-check.yml index f184fae4..4caf6570 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-and-check.yml @@ -23,7 +23,7 @@ jobs: if: steps.system-deps-cache.outputs.cache-hit != 'true' run: | sudo apt-get update - sudo apt-get install -y texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-bibtex-extra pandoc pandoc-citeproc + sudo apt-get install -y texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-bibtex-extra install-r-dependencies: runs-on: ${{ matrix.config.os }} @@ -94,9 +94,27 @@ jobs: /usr/local/texlive/texmf-var /usr/local/texlive/tlpkg/texlive.tlpdb key: ${{ runner.os }}-system-deps-v1 - - name: Check Pandoc Installation + + - name: Restore pandoc + uses: actions/cache@v3 + id: pandoc + with: + path: pandoc-2.19.2-1 + key: pandoc-2.19.2-1 + - name: Download pandoc-2.19.2-1-amd64.deb + if: steps.pandoc.outputs.cache-hit != 'true' run: | - pandoc --version + mkdir pandoc-2.19.2-1 + cd pandoc-2.19.2-1 + wget https://github.com/jgm/pandoc/releases/download/2.19.2/pandoc-2.19.2-1-amd64.deb + - name: Install pandoc + run: | + cd pandoc-2.19.2-1 + sudo dpkg -i pandoc-2.19.2-1-amd64.deb + + - name: Check pandoc version + run: pandoc --version + - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 - name: Execute Gradle build for bibliography From 4eab9b868d962639420b0509b8226b80a1b64f9d Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 21:55:23 +0200 Subject: [PATCH 30/43] Updated GitHub actions --- ...ild-and-check.yml => build-check-test.yml} | 180 +++++------------- 1 file changed, 45 insertions(+), 135 deletions(-) rename .github/workflows/{build-and-check.yml => build-check-test.yml} (52%) diff --git a/.github/workflows/build-and-check.yml b/.github/workflows/build-check-test.yml similarity index 52% rename from .github/workflows/build-and-check.yml rename to .github/workflows/build-check-test.yml index 4caf6570..fee3c9e8 100644 --- a/.github/workflows/build-and-check.yml +++ b/.github/workflows/build-check-test.yml @@ -1,102 +1,19 @@ -name: build +name: build-check-test on: [push, pull_request, workflow_dispatch] jobs: - install-system-dependencies: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Restore System Dependencies Cache - uses: actions/cache@v3 - id: system-deps-cache - with: - path: | - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - /usr/local/texlive/tlpkg/texlive.tlpdb - key: ${{ runner.os }}-system-deps-v1 - - - name: Install System Dependencies - if: steps.system-deps-cache.outputs.cache-hit != 'true' - run: | - sudo apt-get update - sudo apt-get install -y texlive-latex-base texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended texlive-bibtex-extra - - install-r-dependencies: - runs-on: ${{ matrix.config.os }} - name: ${{ matrix.config.os }} (${{ matrix.config.r }}) - strategy: - fail-fast: false - matrix: - config: - - {os: macOS-latest, r: 'release'} - - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - id: r-packages-cache - with: - path: | - ~/.cache/R - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - - - uses: r-lib/actions/setup-r@v2 - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - cache-version: 2 - working-directory: rDNA/rDNA - extra-packages: | - any::devtools - any::testthat - any::roxygen2 - any::ggplot2 - any::roxygen2 - any::igraph - any::ggraph - any::askpass - any::cluster - any::sna - any::ggrepel - any::coda - any::MCMCpack - any::tidygraph - any::heatmaply - any::factoextra - any::MASS - any::pbmcapply - Bibliography: - needs: install-system-dependencies runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-tinytex@v2 - - uses: gradle/wrapper-validation-action@v1 - - name: Restore System Dependencies - uses: actions/cache@v3 - with: - path: | - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - /usr/local/texlive/tlpkg/texlive.tlpdb - key: ${{ runner.os }}-system-deps-v1 - - name: Restore pandoc - uses: actions/cache@v3 + - name: Install LaTeX packages + run: tlmgr install urlbst + + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/cache@v3 id: pandoc with: path: pandoc-2.19.2-1 @@ -117,8 +34,10 @@ jobs: - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle build for bibliography - run: ./gradlew :bibliography:build --info --stacktrace + run: ./gradlew :bibliography:build + - name: Store artifacts uses: actions/upload-artifact@v3 with: @@ -148,31 +67,22 @@ jobs: name: DNA path: build/*.jar + rDNA: - needs: [DNA, install-system-dependencies, install-r-dependencies] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: r-lib/actions/setup-r@v2 - - name: Restore System Dependencies - uses: actions/cache@v3 - with: - path: | - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - /usr/local/texlive/tlpkg/texlive.tlpdb - key: ${{ runner.os }}-system-deps-v1 - - name: Restore R Packages - uses: actions/cache@v3 - with: - path: ~/.cache/R - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + - uses: gradle/wrapper-validation-action@v1 + - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle build for rDNA run: ./gradlew :rDNA:build + - name: Store artifacts uses: actions/upload-artifact@v3 with: @@ -181,8 +91,9 @@ jobs: rDNA-check: runs-on: ${{ matrix.config.os }} - needs: [DNA, install-system-dependencies, install-r-dependencies] + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + strategy: fail-fast: false matrix: @@ -192,40 +103,26 @@ jobs: - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} + env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes + steps: - uses: actions/checkout@v3 + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - - name: Restore System Dependencies - uses: actions/cache@v3 - with: - path: | - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - /usr/local/texlive/tlpkg/texlive.tlpdb - key: ${{ runner.os }}-system-deps-v1 - - name: Restore R Packages - uses: actions/cache@v3 - with: - path: ~/.cache/R - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 - - uses: gradle/wrapper-validation-action@v1 - - name: Setup Gradle - uses: gradle/gradle-build-action@v2.4.2 - - name: Execute Gradle build for rDNA - run: ./gradlew :rDNA:build + - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck needs: check working-directory: rDNA/rDNA + - uses: r-lib/actions/check-r-package@v2 with: working-directory: rDNA/rDNA @@ -233,24 +130,37 @@ jobs: rDNA-test: runs-on: ubuntu-latest - needs: [DNA, install-system-dependencies, install-r-dependencies] steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-r@v2 - - name: Restore System Dependencies - uses: actions/cache@v3 - with: - path: | - /usr/local/texlive - /usr/local/bin/pandoc - /usr/local/texlive/texmf-var - /usr/local/texlive/tlpkg/texlive.tlpdb - key: ${{ runner.os }}-system-deps-v1 + - name: Restore R Packages uses: actions/cache@v3 with: path: ~/.cache/R key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + cache-version: 2 + working-directory: rDNA/rDNA + extra-packages: | + any::ggplot2 + any::roxygen2 + any::igraph + any::ggraph + any::askpass + any::cluster + any::sna + any::ggrepel + any::coda + any::MCMCpack + any::tidygraph + any::heatmaply + any::factoextra + any::MASS + any::pbmcapply + - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 From d78a4ec7e969b4a37f2fadf1a6a3cda8e2cd20dd Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 22:07:59 +0200 Subject: [PATCH 31/43] Updated GitHub actions --- .github/workflows/build-check-test.yml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index fee3c9e8..1a0c9f4f 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -75,6 +75,19 @@ jobs: - uses: r-lib/actions/setup-r@v2 + - name: Restore R Packages + uses: actions/cache@v3 + with: + path: ~/.cache/R + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + cache-version: 2 + working-directory: rDNA/rDNA + extra-packages: | + any::roxygen2 + - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle @@ -117,11 +130,18 @@ jobs: http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true + - name: Restore R Packages + uses: actions/cache@v3 + with: + path: ~/.cache/R + key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck - needs: check + cache-version: 2 working-directory: rDNA/rDNA + extra-packages: | + any::rcmdcheck - uses: r-lib/actions/check-r-package@v2 with: @@ -145,8 +165,8 @@ jobs: cache-version: 2 working-directory: rDNA/rDNA extra-packages: | + any::covr any::ggplot2 - any::roxygen2 any::igraph any::ggraph any::askpass From 2d274adfb1048d8ca14cd6c713c049dc28067b55 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 22:26:45 +0200 Subject: [PATCH 32/43] Updated GitHub actions --- .github/workflows/build-check-test.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index 1a0c9f4f..4e07254a 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -79,20 +79,26 @@ jobs: uses: actions/cache@v3 with: path: ~/.cache/R - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + key: ${{ runner.os }}-r-rDNA-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: r-lib/actions/setup-r-dependencies@v2 with: cache-version: 2 working-directory: rDNA/rDNA extra-packages: | - any::roxygen2 + any::roxygen2 - uses: gradle/wrapper-validation-action@v1 - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 + - name: Restore Gradle Cache + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-v1 + - name: Execute Gradle build for rDNA run: ./gradlew :rDNA:build @@ -134,7 +140,7 @@ jobs: uses: actions/cache@v3 with: path: ~/.cache/R - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + key: ${{ runner.os }}-r-rDNA-check-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: r-lib/actions/setup-r-dependencies@v2 with: @@ -158,7 +164,7 @@ jobs: uses: actions/cache@v3 with: path: ~/.cache/R - key: ${{ runner.os }}-r-${{ hashFiles('**/DESCRIPTION') }}-v1 + key: ${{ runner.os }}-r-rDNA-test-${{ hashFiles('**/DESCRIPTION') }}-v1 - uses: r-lib/actions/setup-r-dependencies@v2 with: @@ -182,6 +188,13 @@ jobs: any::pbmcapply - uses: gradle/wrapper-validation-action@v1 + + - name: Restore Gradle Cache + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-v1 + - name: Setup Gradle uses: gradle/gradle-build-action@v2.4.2 - name: Execute Gradle build for rDNA From 87721a48b26ade386fe208f3e57fd6c537b532cd Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 22:53:32 +0200 Subject: [PATCH 33/43] Updated GitHub actions and gradle for testing --- .github/workflows/build-check-test.yml | 61 ++++++++---------------- rDNA/build.gradle | 8 +--- rDNA/rDNA/tests/testthat/test-backbone.R | 10 ---- 3 files changed, 20 insertions(+), 59 deletions(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index 4e07254a..1a78947a 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -154,55 +154,32 @@ jobs: working-directory: rDNA/rDNA upload-snapshots: true - rDNA-test: + rDNA-coverage: + needs: rDNA runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + + - uses: actions/download-artifact@v3 + with: + name: rDNA + - uses: r-lib/actions/setup-r@v2 + with: + r-version: release - name: Restore R Packages uses: actions/cache@v3 with: path: ~/.cache/R - key: ${{ runner.os }}-r-rDNA-test-${{ hashFiles('**/DESCRIPTION') }}-v1 + key: ${{ runner.os }}-r-rDNA-coverage-${{ hashFiles('**/DESCRIPTION') }}-v1 - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - cache-version: 2 - working-directory: rDNA/rDNA - extra-packages: | - any::covr - any::ggplot2 - any::igraph - any::ggraph - any::askpass - any::cluster - any::sna - any::ggrepel - any::coda - any::MCMCpack - any::tidygraph - any::heatmaply - any::factoextra - any::MASS - any::pbmcapply - - - uses: gradle/wrapper-validation-action@v1 - - - name: Restore Gradle Cache - uses: actions/cache@v3 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-v1 - - - name: Setup Gradle - uses: gradle/gradle-build-action@v2.4.2 - - name: Execute Gradle build for rDNA - run: ./gradlew :rDNA:build - - name: Execute Gradle rDNATest Task - run: ./gradlew :rDNA:rDNATest - - name: Store test results - uses: actions/upload-artifact@v3 - with: - name: rDNA-Test-Results - path: build/reports/tests + - name: Install Dependencies + run: | + install.packages('remotes') + remotes::install_deps(dependencies = TRUE) + install.packages('covr') + + - name: Run Coverage + run: | + Rscript -e 'pkg_tarball <- list.files(pattern = "*.tar.gz", full.names = TRUE); covr::codecov(path = pkg_tarball)' diff --git a/rDNA/build.gradle b/rDNA/build.gradle index 86f78796..513400bb 100644 --- a/rDNA/build.gradle +++ b/rDNA/build.gradle @@ -112,13 +112,7 @@ task rDNATest(dependsOn: [rDNADocument, copyRPackageSourcesToBuildDir, copyJarIn commandLine 'R', '-e', 'devtools::test()' } - // Run code coverage using covr::codecov() on the package source directory - exec { - workingDir tempDir.absolutePath - commandLine 'R', '-e', 'covr::codecov()' - } - - println "R package testing and code coverage completed." + println "R package testing completed." println "Deleting temporary directory..." delete tempDir println "Temporary directory deleted." diff --git a/rDNA/rDNA/tests/testthat/test-backbone.R b/rDNA/rDNA/tests/testthat/test-backbone.R index 290cd09b..c956804c 100644 --- a/rDNA/rDNA/tests/testthat/test-backbone.R +++ b/rDNA/rDNA/tests/testthat/test-backbone.R @@ -2,7 +2,6 @@ context("backbone") test_that("Penalized backbone works", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_init() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") @@ -40,7 +39,6 @@ test_that("Penalized backbone works", { test_that("Plot method works for backbones with penalty", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "penalty", @@ -66,7 +64,6 @@ test_that("Plot method works for backbones with penalty", { test_that("Autoplot method works for backbones with penalty", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "penalty", @@ -89,7 +86,6 @@ test_that("Autoplot method works for backbones with penalty", { test_that("Fixed backbone works", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -126,7 +122,6 @@ test_that("Fixed backbone works", { test_that("Plot method works for fixed backbone size", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -152,7 +147,6 @@ test_that("Plot method works for fixed backbone size", { test_that("Autoplot method works for backbones with fixed size", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -175,7 +169,6 @@ test_that("Autoplot method works for backbones with fixed size", { test_that("Nested backbone works", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -203,7 +196,6 @@ test_that("Nested backbone works", { test_that("Plot method works for nested backbone", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -227,7 +219,6 @@ test_that("Plot method works for nested backbone", { test_that("Autoplot method works for nested backbones", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -247,7 +238,6 @@ test_that("Autoplot method works for nested backbones", { test_that("Evaluate backbone solution works", { testthat::skip_on_cran() - testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_evaluateBackboneSolution( From 82cc74a263857180bcadc722ca3889c30b122919 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 23:06:11 +0200 Subject: [PATCH 34/43] Updated GitHub actions --- .github/workflows/build-check-test.yml | 55 ++++++++++++++++-------- rDNA/rDNA/tests/testthat/test-backbone.R | 10 +++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index 1a78947a..c31e7e5e 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -154,32 +154,49 @@ jobs: working-directory: rDNA/rDNA upload-snapshots: true - rDNA-coverage: - needs: rDNA + rDNA-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - - uses: actions/download-artifact@v3 - with: - name: rDNA - - uses: r-lib/actions/setup-r@v2 - with: - r-version: release - name: Restore R Packages uses: actions/cache@v3 with: path: ~/.cache/R - key: ${{ runner.os }}-r-rDNA-coverage-${{ hashFiles('**/DESCRIPTION') }}-v1 - - - name: Install Dependencies - run: | - install.packages('remotes') - remotes::install_deps(dependencies = TRUE) - install.packages('covr') + key: ${{ runner.os }}-r-rDNA-test-${{ hashFiles('**/DESCRIPTION') }}-v1 - - name: Run Coverage - run: | - Rscript -e 'pkg_tarball <- list.files(pattern = "*.tar.gz", full.names = TRUE); covr::codecov(path = pkg_tarball)' + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + cache-version: 2 + working-directory: rDNA/rDNA + extra-packages: | + any::covr + any::ggplot2 + any::igraph + any::ggraph + any::askpass + any::cluster + any::sna + any::ggrepel + any::coda + any::MCMCpack + any::tidygraph + any::heatmaply + any::factoextra + any::MASS + any::pbmcapply + - uses: gradle/wrapper-validation-action@v1 + + - name: Restore Gradle Cache + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-v1 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2.4.2 + - name: Execute Gradle build for rDNA + run: ./gradlew :rDNA:build + - name: Execute Gradle rDNATest Task + run: ./gradlew :rDNA:rDNATest \ No newline at end of file diff --git a/rDNA/rDNA/tests/testthat/test-backbone.R b/rDNA/rDNA/tests/testthat/test-backbone.R index c956804c..290cd09b 100644 --- a/rDNA/rDNA/tests/testthat/test-backbone.R +++ b/rDNA/rDNA/tests/testthat/test-backbone.R @@ -2,6 +2,7 @@ context("backbone") test_that("Penalized backbone works", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_init() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") @@ -39,6 +40,7 @@ test_that("Penalized backbone works", { test_that("Plot method works for backbones with penalty", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "penalty", @@ -64,6 +66,7 @@ test_that("Plot method works for backbones with penalty", { test_that("Autoplot method works for backbones with penalty", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "penalty", @@ -86,6 +89,7 @@ test_that("Autoplot method works for backbones with penalty", { test_that("Fixed backbone works", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -122,6 +126,7 @@ test_that("Fixed backbone works", { test_that("Plot method works for fixed backbone size", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -147,6 +152,7 @@ test_that("Plot method works for fixed backbone size", { test_that("Autoplot method works for backbones with fixed size", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "fixed", @@ -169,6 +175,7 @@ test_that("Autoplot method works for backbones with fixed size", { test_that("Nested backbone works", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -196,6 +203,7 @@ test_that("Nested backbone works", { test_that("Plot method works for nested backbone", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -219,6 +227,7 @@ test_that("Plot method works for nested backbone", { test_that("Autoplot method works for nested backbones", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_backbone(method = "nested", @@ -238,6 +247,7 @@ test_that("Autoplot method works for nested backbones", { test_that("Evaluate backbone solution works", { testthat::skip_on_cran() + testthat::skip_on_ci() samp <- dna_sample() dna_openDatabase(samp, coderId = 1, coderPassword = "sample") b <- dna_evaluateBackboneSolution( From fa0865a7390594cb559878bcc31d2a3dd9383905 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 23:17:03 +0200 Subject: [PATCH 35/43] Updated GitHub actions --- .github/workflows/build-check-test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index c31e7e5e..1a946be8 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -199,4 +199,6 @@ jobs: - name: Execute Gradle build for rDNA run: ./gradlew :rDNA:build - name: Execute Gradle rDNATest Task - run: ./gradlew :rDNA:rDNATest \ No newline at end of file + run: | + export CI=false + ./gradlew :rDNA:rDNATest \ No newline at end of file From e23a2d709d5f29b367a6dfbbdd9376da316acdf8 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 23:32:32 +0200 Subject: [PATCH 36/43] Updated GitHub actions and updated dna_jar --- .github/workflows/build-check-test.yml | 51 +---------- rDNA/rDNA/R/rDNA.R | 121 ++++--------------------- 2 files changed, 17 insertions(+), 155 deletions(-) diff --git a/.github/workflows/build-check-test.yml b/.github/workflows/build-check-test.yml index 1a946be8..1ad7fc70 100644 --- a/.github/workflows/build-check-test.yml +++ b/.github/workflows/build-check-test.yml @@ -152,53 +152,4 @@ jobs: - uses: r-lib/actions/check-r-package@v2 with: working-directory: rDNA/rDNA - upload-snapshots: true - - rDNA-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: r-lib/actions/setup-r@v2 - - - name: Restore R Packages - uses: actions/cache@v3 - with: - path: ~/.cache/R - key: ${{ runner.os }}-r-rDNA-test-${{ hashFiles('**/DESCRIPTION') }}-v1 - - - uses: r-lib/actions/setup-r-dependencies@v2 - with: - cache-version: 2 - working-directory: rDNA/rDNA - extra-packages: | - any::covr - any::ggplot2 - any::igraph - any::ggraph - any::askpass - any::cluster - any::sna - any::ggrepel - any::coda - any::MCMCpack - any::tidygraph - any::heatmaply - any::factoextra - any::MASS - any::pbmcapply - - uses: gradle/wrapper-validation-action@v1 - - - name: Restore Gradle Cache - uses: actions/cache@v3 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle') }}-v1 - - - name: Setup Gradle - uses: gradle/gradle-build-action@v2.4.2 - - name: Execute Gradle build for rDNA - run: ./gradlew :rDNA:build - - name: Execute Gradle rDNATest Task - run: | - export CI=false - ./gradlew :rDNA:rDNATest \ No newline at end of file + upload-snapshots: true \ No newline at end of file diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index c6b04f5d..c2587f05 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -101,26 +101,20 @@ dna_getHeadlessDna <- function() { dnaEnvironment[["dna"]]$headlessDna } -#' Identify and/or download and install the correct DNA jar file -#' -#' Identify and/or download and install the correct DNA jar file. -#' -#' rDNA requires the installation of a DNA jar file to run properly. While it is -#' possible to store the jar file in the respective working directory, it is -#' preferable to install it in the rDNA library installation directory under -#' \code{java/}. The \code{dna_jar} function attempts to find the version of the -#' jar file that matches the installed \pkg{rDNA} version in the \code{java/} -#' sub-directory of the package library path and return the jar file name -#' including its full path. If this fails, it will try to find the jar file in -#' the current working directory and return its file name. If this fails as -#' well, it will attempt to download the matching jar file from GitHub and store -#' it in the library path and return its file name. If this fails, it will -#' attempt to store the downloaded jar file in the working directory and return -#' its file name. If this fails as well, it will clone the current DNA master -#' code from GitHub to a local temporary directory, build the jar file from -#' source, and attempt to store the built jar file in the library path or, if -#' this fails, in the working directory and return the file name of the jar -#' file. If all of this fails, an error message is thrown. +#' Find the DNA jar file +#' +#' Find the DNA jar file in the library path or working directory. +#' +#' rDNA requires the installation of a DNA jar file to run properly. The jar +#' file is shipped with the rDNA package and is installed in the \code{java/} +#' directory of the package installation directory in the R library tree. The +#' version number of the jar file and the rDNA package must match for DNA and +#' rDNA to be able to work together. The \code{dna_jar} function looks for +#' the jar file in the package installation directory sub-directory and +#' returns its file name with its absolute path. If it cannot be found in the +#' installation directory, the function looks in the current working +#' directory. The function is also called by \code{\link{dna_init}} if the +#' location of the jar file is not provided explicitly. #' #' @return The file name of the jar file that matches the installed \pkg{rDNA} #' version, including full path. @@ -164,91 +158,8 @@ dna_jar <- function() { } }, error = function(e) {success <- FALSE}) - # try to download from GitHub release directory to library path - tryCatch({ - rdna_dir <- dirname(system.file(".", package = "rDNA")) - f <- paste0("https://github.com/leifeld/dna/releases/download/v", v, "/dna-", v, ".jar") - dest <- paste0(rdna_dir, "/java/dna-", v, ".jar") - targetdir <- paste0(rdna_dir, "/java/") - dir.create(targetdir, recursive = TRUE, showWarnings = FALSE) - suppressWarnings(download.file(url = f, - destfile = dest, - mode = "wb", - cacheOK = FALSE, - quiet = TRUE)) - if (file.exists(dest)) { - message("Jar file downloaded from GitHub to library path.") - return(dest) - } - }, error = function(e) {success <- FALSE}) - - # try to download from GitHub release directory to working directory - tryCatch({ - rdna_dir <- dirname(system.file(".", package = "rDNA")) - f <- paste0("https://github.com/leifeld/dna/releases/download/v", v, "/dna-", v, ".jar") - dest <- paste0(getwd(), "/dna-", v, ".jar") - suppressWarnings(download.file(url = f, - destfile = dest, - mode = "wb", - cacheOK = FALSE, - quiet = TRUE)) - if (file.exists(dest)) { - message("Jar file downloaded from GitHub to working directory.") - return(dest) - } - }, error = function(e) {success <- FALSE}) - - # try to download and build from source - tryCatch({ - td <- tempdir() - dest <- paste0(td, "/master.zip") - suppressWarnings(download.file(url = "https://github.com/leifeld/dna/archive/master.zip", - destfile = dest, - mode = "wb", - cacheOK = FALSE, - quiet = TRUE)) - unzip(zipfile = dest, overwrite = TRUE, exdir = td) - output <- file.remove(dest) - gradle <- paste0(td, "/dna-master/gradlew") - Sys.chmod(gradle, mode = "0777", use_umask = TRUE) - oldwd <- getwd() - setwd(paste0(td, "/dna-master/")) - system(paste0(gradle, " build"), ignore.stdout = TRUE, ignore.stderr = TRUE) - setwd(oldwd) - builtjar <- paste0(td, "/dna-master/dna/build/libs/dna-", v, ".jar") - if (file.exists(builtjar)) { - message("DNA source code downloaded and jar file built successfully.") - } - }, error = function(e) {success <- FALSE}) - - # try to copy built jar to library path - tryCatch({ - targetdir <- paste0(find.package("rDNA"), "/java/") - dir.create(targetdir, recursive = TRUE, showWarnings = FALSE) - dest <- paste0(targetdir, "dna-", v, ".jar") - file.copy(from = builtjar, to = targetdir) - if (file.exists(dest)) { - unlink(paste0(td, "/dna-master"), recursive = TRUE) - message("Jar file copied to library path.") - return(dest) - } - }, error = function(e) {success <- FALSE}) - - # try to copy built jar to working directory - tryCatch({ - dest <- paste0(getwd(), "/dna-", v, ".jar") - file.copy(from = builtjar, to = dest) - if (file.exists(dest)) { - unlink(paste0(td, "/dna-master"), recursive = TRUE) - message("Jar file copied to working directory.") - return(dest) - } - }, error = function(e) {success <- FALSE}) - - stop("DNA jar file could not be identified or downloaded. Please download ", - "the DNA jar file matching the version number of rDNA and store it in ", - "the java/ sub-directory of your rDNA library installation path or in ", - "your working directory. Your current rDNA version is ", v, ".") + stop("DNA jar file could not be found in the library path or working " + "directory. Your current rDNA version is ", v, ".") } #' Provides a small sample database From 19b5b59ecc6368901c4d249a56e569a05524caf8 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 23:47:09 +0200 Subject: [PATCH 37/43] Updated dna_jar --- rDNA/rDNA/R/rDNA.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index c2587f05..7a93ee6a 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -158,7 +158,7 @@ dna_jar <- function() { } }, error = function(e) {success <- FALSE}) - stop("DNA jar file could not be found in the library path or working " + stop("DNA jar file could not be found in the library path or working ", "directory. Your current rDNA version is ", v, ".") } From 35c7665d6a0e0272cde86fe568272a3abbc5b0b9 Mon Sep 17 00:00:00 2001 From: leifeld Date: Mon, 19 Aug 2024 23:56:22 +0200 Subject: [PATCH 38/43] Split rDNA code into multiple R files --- rDNA/rDNA/R/dna_backbone.R | 759 +++++ rDNA/rDNA/R/dna_barplot.R | 530 ++++ rDNA/rDNA/R/dna_multiclust.R | 1190 ++++++++ rDNA/rDNA/R/dna_network.R | 1166 ++++++++ rDNA/rDNA/R/dna_phaseTransitions.R | 643 +++++ rDNA/rDNA/R/rDNA.R | 4308 ---------------------------- 6 files changed, 4288 insertions(+), 4308 deletions(-) create mode 100644 rDNA/rDNA/R/dna_backbone.R create mode 100644 rDNA/rDNA/R/dna_barplot.R create mode 100644 rDNA/rDNA/R/dna_multiclust.R create mode 100644 rDNA/rDNA/R/dna_network.R create mode 100644 rDNA/rDNA/R/dna_phaseTransitions.R diff --git a/rDNA/rDNA/R/dna_backbone.R b/rDNA/rDNA/R/dna_backbone.R new file mode 100644 index 00000000..e5db7878 --- /dev/null +++ b/rDNA/rDNA/R/dna_backbone.R @@ -0,0 +1,759 @@ +#' Compute and retrieve the backbone and redundant set +#' +#' Compute and retrieve the backbone and redundant set of a discourse network. +#' +#' The dna_backbone function applies a simulated annealing algorithm to the +#' discourse network to partition the set of second-mode entities (e.g., +#' concepts) into a backbone set and a complementary redundant set. Three +#' methods are available: +#' \itemize{ +#' \item A simulated annealing algorithm with a penalty. You can play with +#' different penalties and see how they affect the size of your backbone +#' set. +#' \item A modified simulated annealing algorithm for a fixed number of +#' backbone entities to retain. This is computationally simpler, but you +#' have to know how large the set should be. +#' \item A fast and greedy nested algorithm, which evaluates all possible +#' fixed backbone solutions, i.e., for all sizes, and provides a nested +#' hierarchy of entities on the second mode. This algorithm may stay below +#' the optimum and is only an approximation but provides insights into the +#' hierarchy of concepts and their relative importance. +#' } +#' +#' The \code{autoplot} function requires the ggplot2 package and can plot +#' algorithm diagnostics and the hierarchy of entities as a dendrogram, +#' depending on the method that was chosen. The \code{plot} function can do the +#' same thing, just using base plots, not ggplot2. +#' +#' The \code{dna_evaluateBackboneSolution} function computes the spectral loss +#' for an arbitrary backbone and its complement, the redundant set, specified by +#' the user. For example, the user can evaluate how much structure would be lost +#' if the second mode was composed only of the concepts provided to this +#' function. This can be used to compare how useful different codebook models +#' are. The penalty parameter \code{p} applies a penalty factor to the spectral +#' loss. The default value of \code{0} switches off the penalty as it is usually +#' not needed to evaluate a specific solution. The backbone set can be supplied +#' as a vector of character objects, for example concepts. +#' +#' @param method The backbone algorithm used to compute the results. Several +#' methods are available: +#' \itemize{ +#' \item \code{"nested"}: A relatively fast, deterministic algorithm that +#' produces the full hierarchy of entities. It starts with a complete +#' backbone set resembling the full network. There are as many iterations +#' as entities on the second mode. In each iteration, the entity whose +#' removal would yield the smallest backbone loss is moved from the +#' backbone set into the redundant set, and the (unpenalized) spectral +#' loss is recorded. This creates a solution for all backbone sizes, where +#' each backbone set is fully nested in the next larger backbone set. The +#' solution usually resembles an unconstrained solution where nesting is +#' not required, but in some cases the loss of a non-nested solution may be +#' larger at a given level or number of elements in the backbone set. +#' \item \code{"fixed"}: Simulated annealing with a fixed number of elements +#' in the backbone set (i.e., only lateral changes are possible) and +#' without penalty. This method may yield more optimal solutions than the +#' nested algorithm because it does not require a strict hierarchy. +#' However, it produces an approximation of the global optimum and is +#' slower than the nested method. With this method, you can specify that +#' backbone set should have, for example, exactly 10 concepts. Then fewer +#' iterations are necessary than with the penalty method because the search +#' space is smaller. The backbone set size is defined in the +#' \code{"backboneSize"} argument. +#' \item \code{"penalty"}: Simulated annealing with a variable number of +#' elements in the backbone set. The solution is stabilized by a penalty +#' parameter (see \code{"penalty"} argument). This algorithm takes longest +#' to compute for a single solution, and it is only an approximation, but +#' it considers slightly larger or smaller backbone sets if the solution is +#' better, thus this algorithm adds some flexibility. It requires more +#' iterations than the fixed method for achieving the same quality. +#' } +#' @param backboneSize The number of elements in the backbone set, as a fixed +#' parameter. Only used when \code{method = "fixed"}. +#' @param penalty The penalty parameter for large backbone sets. The larger the +#' value, the more strongly larger backbone sets are punished and the smaller +#' the resulting backbone is. Try out different values to find the right size +#' of the backbone set. Reasonable values could be \code{2.5}, \code{5}, +#' \code{7.5}, or \code{12}, for example. The minimum is \code{0.0}, which +#' imposes no penalty on the size of the backbone set and produces a redundant +#' set with only one element. Start with \code{0.0} if you want to weed out a +#' single concept and subsequently increase the penalty to include more items +#' in the redundant set and shrink the backbone further. Only used when +#' \code{method = "penalty"}. +#' @param iterations The number of iterations of the simulated annealing +#' algorithm. More iterations take more time but may lead to better +#' optimization results. Only used when \code{method = "penalty"} or +#' \code{method = "fixed"}. +#' @param qualifierAggregation The aggregation rule for the \code{qualifier} +#' variable. This must be \code{"ignore"} (for ignoring the qualifier +#' variable), \code{"congruence"} (for recording a network tie only if both +#' nodes have the same qualifier value in the binary case or for recording the +#' similarity between the two nodes on the qualifier variable in the integer +#' case), \code{"conflict"} (for recording a network tie only if both nodes +#' have a different qualifier value in the binary case or for recording the +#' distance between the two nodes on the qualifier variable in the integer +#' case), or \code{"subtract"} (for subtracting the conflict tie value from +#' the congruence tie value in each dyad; note that negative values will be +#' replaced by \code{0} in the backbone calculation). +#' @param normalization Normalization of edge weights. Valid settings are +#' \code{"no"} (for switching off normalization), \code{"average"} (for +#' average activity normalization), \code{"jaccard"} (for Jaccard coefficient +#' normalization), and \code{"cosine"} (for cosine similarity normalization). +#' @param fileFormat An optional file format specification for saving the +#' backbone results to a file instead of returning an object. Valid values +#' are \code{"json"}, \code{"xml"}, and \code{NULL} (for returning the results +#' instead of writing them to a file). +#' @inheritParams dna_network +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' # compute backbone and redundant set using penalised spectral loss +#' b <- dna_backbone(method = "penalty", +#' penalty = 3.5, +#' iterations = 10000, +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' qualifierAggregation = "subtract", +#' normalization = "average") +#' +#' b # display main results +#' +#' # extract results from the object +#' b$backbone # show the set of backbone concepts +#' b$redundant # show the set of redundant concepts +#' b$unpenalized_backbone_loss # spectral loss between full and backbone network +#' b$unpenalized_redundant_loss # spectral loss of redundant network +#' b$backbone_network # show the backbone network +#' b$redundant_network # show the redundant network +#' b$full_network # show the full network +#' +#' # plot diagnostics with base R +#' plot(b, ma = 500) +#' +#' # arrange plots in a 2 x 2 view +#' par(mfrow = c(2, 2)) +#' plot(b) +#' +#' # plot diagnostics with ggplot2 +#' library("ggplot2") +#' p <- autoplot(b) +#' p +#' +#' # pick a specific diagnostic +#' p[[3]] +#' +#' # use the patchwork package to arrange the diagnostics in a single plot +#' library("patchwork") +#' new_plot <- p[[1]] + p[[2]] + p[[3]] + p[[4]] +#' new_plot & theme_grey() + theme(legend.position = "bottom") +#' +#' # use the gridExtra package to arrange the diagnostics in a single plot +#' library("gridExtra") +#' grid.arrange(p[[1]], p[[2]], p[[3]], p[[4]]) +#' +#' # compute backbone with fixed size (here: 4 concepts) +#' b <- dna_backbone(method = "fixed", +#' backboneSize = 4, +#' iterations = 2000, +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' qualifierAggregation = "subtract", +#' normalization = "average") +#' b +#' +#' # compute backbone with a nested structure and plot dendrogram +#' b <- dna_backbone(method = "nested", +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' qualifierAggregation = "subtract", +#' normalization = "average") +#' b +#' plot(b) +#' autoplot(b) +#' } +#' +#' @author Philip Leifeld, Tim Henrichsen +#' +#' @rdname dna_backbone +#' @importFrom rJava .jarray +#' @importFrom rJava .jcall +#' @importFrom rJava .jnull +#' @importFrom rJava J +#' @export +dna_backbone <- function(method = "nested", + backboneSize = 1, + penalty = 3.5, + iterations = 10000, + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "subtract", + normalization = "average", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE, + fileFormat = NULL, + outfile = NULL) { + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + if (is.null(fileFormat)) { + fileFormat <- .jnull(class = "java/lang/String") + } + if (is.null(outfile)) { + outfile <- .jnull(class = "java/lang/String") + } + + # call rBackbone function to compute results + .jcall(dnaEnvironment[["dna"]]$headlessDna, + "V", + "rBackbone", + method, + as.integer(backboneSize), + as.double(penalty), + as.integer(iterations), + statementType, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes, + outfile, + fileFormat + ) + + exporter <- .jcall(dnaEnvironment[["dna"]]$headlessDna, "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored + if (!is.null(outfile) && !is.null(fileFormat) && is.character(outfile) && is.character(fileFormat) && fileFormat %in% c("json", "xml")) { + message("File exported.") + } else if (method[1] %in% c("penalty", "fixed")) { + result <- .jcall(exporter, "Lexport/SimulatedAnnealingBackboneResult;", "getSimulatedAnnealingBackboneResult", simplify = TRUE) + # create a list with various results + l <- list() + l$penalty <- .jcall(result, "D", "getPenalty") + if (method[1] == "fixed") { + l$backbone_size <- as.integer(backboneSize) + } else { + l$backbone_size <- as.integer(NA) + } + l$iterations <- .jcall(result, "I", "getIterations") + l$backbone <- .jcall(result, "[S", "getBackboneEntities") + l$redundant <- .jcall(result, "[S", "getRedundantEntities") + l$unpenalized_backbone_loss <- .jcall(result, "D", "getUnpenalizedBackboneLoss") + l$unpenalized_redundant_loss <- .jcall(result, "D", "getUnpenalizedRedundantLoss") + rn <- .jcall(result, "[S", "getLabels") + + # store the three matrices in the result list + fullmat <- .jcall(result, "[[D", "getFullNetwork", simplify = TRUE) + rownames(fullmat) <- rn + colnames(fullmat) <- rn + l$full_network <- fullmat + backbonemat <- .jcall(result, "[[D", "getBackboneNetwork", simplify = TRUE) + rownames(backbonemat) <- rn + colnames(backbonemat) <- rn + l$backbone_network <- backbonemat + redundantmat <- .jcall(result, "[[D", "getRedundantNetwork", simplify = TRUE) + rownames(redundantmat) <- rn + colnames(redundantmat) <- rn + l$redundant_network <- redundantmat + + # store diagnostics per iteration as a data frame + d <- data.frame(iteration = 1:.jcall(result, "I", "getIterations"), + temperature = .jcall(result, "[D", "getTemperature"), + acceptance_prob = .jcall(result, "[D", "getAcceptanceProbability"), + acceptance = .jcall(result, "[I", "getAcceptance"), + penalized_backbone_loss = .jcall(result, "[D", "getPenalizedBackboneLoss"), + proposed_backbone_size = .jcall(result, "[I", "getProposedBackboneSize"), + current_backbone_size = .jcall(result, "[I", "getCurrentBackboneSize"), + optimal_backbone_size = .jcall(result, "[I", "getOptimalBackboneSize"), + acceptance_ratio_ma = .jcall(result, "[D", "getAcceptanceRatioMovingAverage")) + + l$diagnostics <- d + + # store start date/time, end date/time, number of statements, call, and class label in each network matrix + start <- as.POSIXct(.jcall(result, "J", "getStart"), origin = "1970-01-01") # add the start date/time of the result as an attribute to the matrices + attributes(l$full_network)$start <- start + attributes(l$backbone_network)$start <- start + attributes(l$redundant_network)$start <- start + stop <- as.POSIXct(.jcall(result, "J", "getStop"), origin = "1970-01-01") # add the end date/time of the result as an attribute to the matrices + attributes(l$full_network)$stop <- stop + attributes(l$backbone_network)$stop <- stop + attributes(l$redundant_network)$stop <- stop + attributes(l$full_network)$numStatements <- .jcall(result, "I", "getNumStatements") # add the number of filtered statements the matrix is based on as an attribute to the matrix + attributes(l$full_network)$call <- match.call() + attributes(l$backbone_network)$call <- match.call() + attributes(l$redundant_network)$call <- match.call() + attributes(l)$method <- method[1] + class(l$full_network) <- c("dna_network_onemode", class(l$full_network)) + class(l$backbone_network) <- c("dna_network_onemode", class(l$backbone_network)) + class(l$redundant_network) <- c("dna_network_onemode", class(l$redundant_network)) + class(l) <- c("dna_backbone", class(l)) + return(l) + } else if (method[1] == "nested") { + result <- .jcall(exporter, "Lexport/NestedBackboneResult;", "getNestedBackboneResult", simplify = TRUE) + d <- data.frame(i = .jcall(result, "[I", "getIteration"), + entity = .jcall(result, "[S", "getEntities"), + backboneLoss = .jcall(result, "[D", "getBackboneLoss"), + redundantLoss = .jcall(result, "[D", "getRedundantLoss"), + statements = .jcall(result, "[I", "getNumStatements")) + rownames(d) <- NULL + attributes(d)$numStatementsFull <- .jcall(result, "I", "getNumStatementsFull") + attributes(d)$start <- as.POSIXct(.jcall(result, "J", "getStart"), origin = "1970-01-01") # add the start date/time of the result as an attribute + attributes(d)$stop <- as.POSIXct(.jcall(result, "J", "getStop"), origin = "1970-01-01") # add the end date/time of the result as an attribute + attributes(d)$method <- "nested" + class(d) <- c("dna_backbone", class(d)) + return(d) + } +} + +#' @rdname dna_backbone +#' @param x A \code{"dna_backbone"} object. +#' @param trim Number of maximum characters to display in entity labels. Labels +#' with more characters are truncated, and the last character is replaced by +#' an asterisk (\code{*}). +#' @export +print.dna_backbone <- function(x, trim = 50, ...) { + method <- attributes(x)$method + cat(paste0("Backbone method: ", method, ".\n\n")) + if (method %in% c("penalty", "fixed")) { + if (method == "penalty") { + cat(paste0("Penalty: ", x$penalty, ". Iterations: ", x$iterations, ".\n\n")) + } else { + cat(paste0("Backbone size: ", x$backbone_size, ". Iterations: ", x$iterations, ".\n\n")) + } + cat(paste0("Backbone set (loss: ", round(x$unpenalized_backbone_loss, 4), "):\n")) + cat(paste(1:length(x$backbone), x$backbone), sep = "\n") + cat(paste0("\nRedundant set (loss: ", round(x$unpenalized_redundant_loss, 4), "):\n")) + cat(paste(1:length(x$redundant), x$redundant), sep = "\n") + } else if (method == "nested") { + x2 <- x + x2$entity <- sapply(x2$entity, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + print(as.data.frame(x2), row.names = FALSE) + } +} + +#' @param ma Number of iterations to compute moving average. +#' @rdname dna_backbone +#' @importFrom graphics lines +#' @importFrom stats filter +#' @importFrom rlang .data +#' @export +plot.dna_backbone <- function(x, ma = 500, ...) { + + if (attr(x, "method") != "nested") { + # temperature and acceptance probability + plot(x = x$diagnostics$iteration, + y = x$diagnostics$temperature, + col = "red", + type = "l", + lwd = 3, + xlab = "Iteration", + ylab = "Acceptance probability", + main = "Temperature and acceptance probability") + # note that better solutions are coded as -1 and need to be skipped: + lines(x = x$diagnostics$iteration[x$diagnostics$acceptance_prob >= 0], + y = x$diagnostics$acceptance_prob[x$diagnostics$acceptance_prob >= 0]) + + # spectral distance between full network and backbone network per iteration + bb_loss <- stats::filter(x$diagnostics$penalized_backbone_loss, + rep(1 / ma, ma), + sides = 1) + if (attributes(x)$method == "penalty") { + yl <- "Penalized backbone loss" + ti <- "Penalized spectral backbone distance" + } else { + yl <- "Backbone loss" + ti <- "Spectral backbone distance" + } + plot(x = x$diagnostics$iteration, + y = bb_loss, + type = "l", + xlab = "Iteration", + ylab = yl, + main = ti) + + # number of concepts in the backbone solution per iteration + current_size_ma <- stats::filter(x$diagnostics$current_backbone_size, + rep(1 / ma, ma), + sides = 1) + optimal_size_ma <- stats::filter(x$diagnostics$optimal_backbone_size, + rep(1 / ma, ma), + sides = 1) + plot(x = x$diagnostics$iteration, + y = current_size_ma, + ylim = c(min(c(current_size_ma, optimal_size_ma), na.rm = TRUE), + max(c(current_size_ma, optimal_size_ma), na.rm = TRUE)), + type = "l", + xlab = "Iteration", + ylab = paste0("Number of elements (MA, last ", ma, ")"), + main = "Backbone size (red = best)") + lines(x = x$diagnostics$iteration, y = optimal_size_ma, col = "red") + + # ratio of recent acceptances + accept_ratio <- stats::filter(x$diagnostics$acceptance, + rep(1 / ma, ma), + sides = 1) + plot(x = x$diagnostics$iteration, + y = accept_ratio, + type = "l", + xlab = "Iteration", + ylab = paste("Acceptance ratio in the last", ma, "iterations"), + main = "Acceptance ratio") + } else { # create hclust object + # define merging pattern: negative numbers are leaves, positive are merged clusters + merges_clust <- matrix(nrow = nrow(x) - 1, ncol = 2) + + merges_clust[1,1] <- -nrow(x) + merges_clust[1,2] <- -(nrow(x) - 1) + + for (i in 2:(nrow(x) - 1)) { + merges_clust[i, 1] <- -(nrow(x) - i) + merges_clust[i, 2] <- i - 1 + } + + # Initialize empty object + a <- list() + + # Add merges + a$merge <- merges_clust + + # Define merge heights + a$height <- x$backboneLoss[1:nrow(x) - 1] + + # Order of leaves + a$order <- 1:nrow(x) + + # Labels of leaves + a$labels <- rev(x$entity) + + # Define hclust class + class(a) <- "hclust" + + plot(a, ylab = "") + } +} + +#' @rdname dna_backbone +#' @param object A \code{"dna_backbone"} object. +#' @param ... Additional arguments. +#' @importFrom ggplot2 autoplot +#' @importFrom ggplot2 ggplot +#' @importFrom ggplot2 aes +#' @importFrom ggplot2 geom_line +#' @importFrom ggplot2 ylab +#' @importFrom ggplot2 xlab +#' @importFrom ggplot2 ggtitle +#' @importFrom ggplot2 theme_bw +#' @importFrom ggplot2 theme +#' @importFrom ggplot2 coord_flip +#' @importFrom ggplot2 scale_x_continuous +#' @importFrom ggplot2 scale_y_continuous +#' @importFrom rlang .data +#' @export +autoplot.dna_backbone <- function(object, ..., ma = 500) { + if (attr(object, "method") != "nested") { + bd <- object$diagnostics + bd$bb_loss <- stats::filter(bd$penalized_backbone_loss, rep(1 / ma, ma), sides = 1) + bd$current_size_ma <- stats::filter(bd$current_backbone_size, rep(1 / ma, ma), sides = 1) + bd$optimal_size_ma <- stats::filter(bd$optimal_backbone_size, rep(1 / ma, ma), sides = 1) + bd$accept_ratio <- stats::filter(bd$acceptance, rep(1 / ma, ma), sides = 1) + + # temperature and acceptance probability + g_accept <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["temperature"]], x = .data[["iteration"]])) + + ggplot2::geom_line(color = "#a50f15") + + ggplot2::geom_line(data = bd[bd$acceptance_prob >= 0, ], + ggplot2::aes(y = .data[["acceptance_prob"]], x = .data[["iteration"]])) + + ggplot2::ylab("Acceptance probability") + + ggplot2::xlab("Iteration") + + ggplot2::ggtitle("Temperature and acceptance probability") + + ggplot2::theme_bw() + + # spectral distance between full network and backbone network per iteration + if (attributes(object)$method == "penalty") { + yl <- "Penalized backbone loss" + ti <- "Penalized spectral backbone distance" + } else { + yl <- "Backbone loss" + ti <- "Spectral backbone distance" + } + g_loss <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["bb_loss"]], x = .data[["iteration"]])) + + ggplot2::geom_line() + + ggplot2::ylab(yl) + + ggplot2::xlab("Iteration") + + ggplot2::ggtitle(ti) + + ggplot2::theme_bw() + + # number of concepts in the backbone solution per iteration + d <- data.frame(iteration = rep(bd$iteration, 2), + size = c(bd$current_size_ma, bd$optimal_size_ma), + Criterion = c(rep("Current iteration", nrow(bd)), + rep("Best solution", nrow(bd)))) + g_size <- ggplot2::ggplot(d, ggplot2::aes(y = .data[["size"]], x = .data[["iteration"]], color = .data[["Criterion"]])) + + ggplot2::geom_line() + + ggplot2::ylab(paste0("Number of elements (MA, last ", ma, ")")) + + ggplot2::xlab("Iteration") + + ggplot2::ggtitle("Backbone size") + + ggplot2::theme_bw() + + ggplot2::theme(legend.position = "bottom") + + # ratio of recent acceptances + g_ar <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["accept_ratio"]], x = .data[["iteration"]])) + + ggplot2::geom_line() + + ggplot2::ylab(paste("Acceptance ratio in the last", ma, "iterations")) + + ggplot2::xlab("Iteration") + + ggplot2::ggtitle("Acceptance ratio") + + ggplot2::theme_bw() + + # wrap in list + plots <- list(g_accept, g_loss, g_size, g_ar) + return(plots) + } else { # create hclust object + # define merging pattern: negative numbers are leaves, positive are merged clusters + merges_clust <- matrix(nrow = nrow(object) - 1, ncol = 2) + + merges_clust[1,1] <- -nrow(object) + merges_clust[1,2] <- -(nrow(object) - 1) + + for (i in 2:(nrow(object) - 1)) { + merges_clust[i, 1] <- -(nrow(object) - i) + merges_clust[i, 2] <- i - 1 + } + + # Initialize empty object + a <- list() + + # Add merges + a$merge <- merges_clust + + # Define merge heights + a$height <- object$backboneLoss[1:nrow(object) - 1] + height <- a$height + + # Order of leaves + a$order <- 1:nrow(object) + + # Labels of leaves + a$labels <- rev(object$entity) + + # Define hclust class + class(a) <- "hclust" + + # ensure ggraph is installed, otherwise throw error (better than importing it to avoid hard dependency) + if (!requireNamespace("ggraph", quietly = TRUE)) { + stop("The 'ggraph' package is required for plotting nested backbone dendrograms with 'ggplot2' but was not found. Consider installing it.") + } + + g_clust <- ggraph::ggraph(graph = a, + layout = "dendrogram", + circular = FALSE, + height = height) + # TODO @Tim: "height" does not seem to exist + ggraph::geom_edge_elbow() + + ggraph::geom_node_point(aes(filter = .data[["leaf"]])) + + ggplot2::theme_bw() + + ggplot2::theme(panel.border = element_blank(), + axis.title = element_blank(), + panel.grid.major = element_blank(), + panel.grid.minor = element_blank(), + axis.line = element_blank(), + axis.text.y = element_text(size = 6), + axis.ticks.y = element_blank()) + + ggplot2::scale_x_continuous(breaks = seq(0, nrow(object) - 1, by = 1), + labels = rev(object$entity)) + + ggplot2::scale_y_continuous(expand = c(0, 0.01)) + + ggplot2::coord_flip() + + return(g_clust) + } +} + +#' @param backboneEntities A vector of character values to be included in the +#' backbone. The function will compute the spectral loss between the full +#' network and the network composed only of those entities on the second mode +#' that are contained in this vector. +#' @param p The penalty parameter. The default value of \code{0} means no +#' penalty for backbone size is applied. +#' @inheritParams dna_backbone +#' @return A vector with two numeric values: the backbone and redundant loss. +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' dna_evaluateBackboneSolution( +#' c("There should be legislation to regulate emissions.", +#' "Emissions legislation should regulate CO2.") +#' ) +#' } +#' +#' @rdname dna_backbone +#' @importFrom rJava .jarray +#' @importFrom rJava .jcall +#' @importFrom rJava .jnull +#' @export +dna_evaluateBackboneSolution <- function(backboneEntities, + p = 0, + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "subtract", + normalization = "average", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE) { + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + + # call rBackbone function to compute results + result <- .jcall(dnaEnvironment[["dna"]]$headlessDna, + "[D", + "rEvaluateBackboneSolution", + .jarray(backboneEntities), + as.integer(p), + statementType, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes + ) + names(result) <- c("backbone loss", "redundant loss") + return(result) +} \ No newline at end of file diff --git a/rDNA/rDNA/R/dna_barplot.R b/rDNA/rDNA/R/dna_barplot.R new file mode 100644 index 00000000..ecc24ee2 --- /dev/null +++ b/rDNA/rDNA/R/dna_barplot.R @@ -0,0 +1,530 @@ +#' Generate the data necessary for creating a barplot for a variable +#' +#' Generate the data necessary for creating a barplot for a variable. +#' +#' Create a \code{dna_barplot} object, which contains a data frame with +#' entity value frequencies grouped by the levels of a qualifier variable. +#' The qualifier variable is optional. +#' +#' @param variable The variable for which the barplot will be generated. There +#' will be one bar per entity label of this variable. +#' @param qualifier A boolean (binary) or integer variable to group the value +#' frequencies by. Can be \code{NULL} to skip the grouping. +#' @inheritParams dna_network +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' # compute barplot data +#' b <- dna_barplot(statementType = "DNA Statement", +#' variable = "concept", +#' qualifier = "agreement") +#' b +#' } +#' +#' @author Philip Leifeld +#' +#' @rdname dna_barplot +#' @importFrom rJava .jarray +#' @importFrom rJava .jcall +#' @importFrom rJava .jevalArray +#' @importFrom rJava .jnull +#' @importFrom rJava is.jnull +#' @export +dna_barplot <- function(statementType = "DNA Statement", + variable = "concept", + qualifier = "agreement", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE) { + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + + # call rBarplotData function to compute results + b <- .jcall(dnaEnvironment[["dna"]]$headlessDna, + "Lexport/BarplotResult;", + "rBarplotData", + statementType, + variable, + qualifier, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes, + simplify = TRUE) + + at <- .jcall(b, "[[Ljava/lang/String;", "getAttributes") + at <- t(sapply(at, FUN = .jevalArray)) + + counts <- .jcall(b, "[[I", "getCounts") + counts <- t(sapply(counts, FUN = .jevalArray)) + if (nrow(counts) < nrow(at)) { + counts <- t(counts) + } + + results <- data.frame(.jcall(b, "[S", "getValues"), + counts, + at) + + intValues <- .jcall(b, "[I", "getIntValues") + intColNames <- intValues + if (is.jnull(qualifier)) { + intValues <- integer(0) + intColNames <- "Frequency" + } + + atVar <- .jcall(b, "[S", "getAttributeVariables") + + colnames(results) <- c("Entity", intColNames, atVar) + + attributes(results)$variable <- .jcall(b, "S", "getVariable") + attributes(results)$intValues <- intValues + attributes(results)$attributeVariables <- atVar + + class(results) <- c("dna_barplot", class(results)) + + return(results) +} + +#' Print a \code{dna_barplot} object +#' +#' Show details of a \code{dna_barplot} object. +#' +#' Print the data frame returned by the \code{\link{dna_barplot}} function. +#' +#' @param x A \code{dna_barplot} object, as returned by the +#' \code{\link{dna_barplot}} function. +#' @param trim Number of maximum characters to display in entity labels. +#' Entities with more characters are truncated, and the last character is +#' replaced by an asterisk (\code{*}). +#' @param attr Display attributes, such as the name of the variable and the +#' levels of the qualifier variable if available. +#' @param ... Additional arguments. Currently not in use. +#' +#' @author Philip Leifeld +#' +#' @rdname dna_barplot +#' @export +print.dna_barplot <- function(x, trim = 30, attr = TRUE, ...) { + x2 <- x + if (isTRUE(attr)) { + cat("Variable:", attr(x2, "variable")) + intVal <- attr(x2, "intValues") + if (length(intVal) > 0) { + cat(".\nQualifier levels:", paste(intVal, collapse = ", ")) + } else { + cat(".\nNo qualifier variable") + } + cat(".\n") + } + x2$Entity <- sapply(x2$Entity, function(e) if (nchar(e) > trim) paste0(substr(e, 1, trim - 1), "*") else e) + class(x2) <- "data.frame" + print(x2) +} + +#' Plot \code{dna_barplot} object. +#' +#' Plot a barplot generated from \code{\link{dna_barplot}}. +#' +#' This function plots \code{dna_barplot} objects generated by the +#' \code{\link{dna_barplot}} function. It plots agreement and disagreement with +#' DNA statements for different entities such as \code{"concept"}, +#' \code{"organization"}, or \code{"person"}. Colors can be modified before +#' plotting (see examples). +#' +#' @param object A \code{dna_barplot} object. +#' @param ... Additional arguments; currently not in use. +#' @param lab.pos,lab.neg Names for (dis-)agreement labels. +#' @param lab Should (dis-)agreement labels and title be displayed? +#' @param colors If \code{TRUE}, the \code{Colors} column in the +#' \code{dna_barplot} object will be used to fill the bars. Also accepts +#' character objects matching one of the attribute variables of the +#' \code{dna_barplot} object. +#' @param fontSize Text size in pt. +#' @param barWidth Thickness of the bars. Bars will touch when set to \code{1}. +#' When set to \code{0.5}, space between two bars is the same as thickness of +#' bars. +#' @param axisWidth Thickness of the x-axis which separates agreement from +#' disagreement. +#' @param truncate Sets the number of characters to which axis labels should be +#' truncated. +#' @param exclude.min Reduces the plot to entities with a minimum frequency of +#' statements. +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' # compute barplot data +#' b <- dna_barplot(statementType = "DNA Statement", +#' variable = "concept", +#' qualifier = "agreement") +#' +#' # plot barplot with ggplot2 +#' library("ggplot2") +#' autoplot(b) +#' +#' # use entity colours (here: colors of organizations as an illustration) +#' b <- dna_barplot(statementType = "DNA Statement", +#' variable = "organization", +#' qualifier = "agreement") +#' autoplot(b, colors = TRUE) +#' +#' # edit the colors before plotting +#' b$Color[b$Type == "NGO"] <- "red" # change NGO color to red +#' b$Color[b$Type == "Government"] <- "blue" # change government color to blue +#' autoplot(b, colors = TRUE) +#' +#' # use an attribute, such as type, to color the bars +#' autoplot(b, colors = "Type") + +#' scale_colour_manual(values = "black") +#' +#' # replace colors for the three possible actor types with custom colors +#' autoplot(b, colors = "Type") + +#' scale_fill_manual(values = c("red", "blue", "green")) + +#' scale_colour_manual(values = "black") +#' } +#' +#' @author Johannes B. Gruber, Tim Henrichsen +#' +#' @rdname dna_barplot +#' @importFrom ggplot2 autoplot +#' @importFrom ggplot2 ggplot +#' @importFrom ggplot2 aes +#' @importFrom ggplot2 geom_line +#' @importFrom ggplot2 theme_minimal +#' @importFrom ggplot2 theme +#' @importFrom ggplot2 geom_bar +#' @importFrom ggplot2 position_stack +#' @importFrom ggplot2 coord_flip +#' @importFrom ggplot2 element_blank +#' @importFrom ggplot2 element_text +#' @importFrom ggplot2 scale_color_identity +#' @importFrom ggplot2 scale_fill_identity +#' @importFrom ggplot2 geom_text +#' @importFrom ggplot2 .pt +#' @importFrom ggplot2 annotate +#' @importFrom ggplot2 scale_x_discrete +#' @importFrom utils stack +#' @importFrom grDevices col2rgb +#' @importFrom rlang .data +#' @export +autoplot.dna_barplot <- function(object, + ..., + lab.pos = "Agreement", + lab.neg = "Disagreement", + lab = TRUE, + colors = FALSE, + fontSize = 12, + barWidth = 0.6, + axisWidth = 1.5, + truncate = 40, + exclude.min = NULL) { + + + if (!("dna_barplot" %in% class(object))) { + stop("Invalid data object. Please compute a dna_barplot object via the ", + "dna_barplot function before plotting.") + } + + if (!("Entity" %in% colnames(object))) { + stop("dna_barplot object does not have a \'Entity\' variable. Please ", + "compute a new dna_barplot object via the dna_barplot function before", + " plotting.") + } + + if (isTRUE(colors) & !("Color" %in% colnames(object)) | + is.character(colors) & !(colors %in% colnames(object))) { + colors <- FALSE + warning("No color variable found in dna_barplot object. Colors will be", + " ignored.") + } + + if (!is.numeric(truncate)) { + truncate <- Inf + warning("No numeric value provided for trimming of entities. Truncation ", + "will be ignored.") + } + + # Get qualifier values + w <- attr(object, "intValues") + + if (!all(w %in% colnames(object))) { + stop("dna_barplot object does not include all qualifier values of the ", + "statement type. Please compute a new dna_barplot object via the ", + "dna_barplot function.") + } + + # Check if qualifier is binary + binary <- all(w %in% c(0, 1)) + + # Compute total values per entity + object$sum <- rowSums(object[, colnames(object) %in% w]) + + # Exclude minimum number of statements per entity + if (is.numeric(exclude.min)) { + if (exclude.min > max(object$sum)) { + exclude.min <- NULL + warning("Value provided in exclude.min is higher than maximum frequency ", + "of entity (", max(object$sum), "). Will ignore exclude.min.") + } else { + object <- object[object$sum >= exclude.min, ] + } + } + + # Stack agreement and disagreement + object2 <- cbind(object$Entity, utils::stack(object, select = colnames(object) %in% w)) + colnames(object2) <- c("entity", "frequency", "agreement") + + object <- object[order(object$sum, decreasing = TRUE), ] + + object2$entity <- factor(object2$entity, levels = rev(object$Entity)) + + # Get colors + if (isTRUE(colors)) { + object2$color <- object$Color[match(object2$entity, object$Entity)] + object2$text_color <- "black" + # Change text color to white in case of dark bar colors + object2$text_color[sum(grDevices::col2rgb(object2$color) * c(299, 587, 114)) / 1000 < 123] <- "white" + } else if (is.character(colors)) { + object2$color <- object[, colors][match(object2$entity, object$Entity)] + object2$text_color <- "black" + } else { + object2$color <- "white" + object2$text_color <- "black" + } + + + if (binary) { + # setting disagreement as -1 instead 0 + object2$agreement <- ifelse(object2$agreement == 0, -1, 1) + # recode frequency in positive and negative + object2$frequency <- object2$frequency * as.integer(object2$agreement) + + # generate position of bar labels + offset <- (max(object2$frequency) + abs(min(object2$frequency))) * 0.05 + offset <- ifelse(offset < 0.5, 0.5, offset) # offset should be at least 0.5 + if (offset > abs(min(object2$frequency))) { + offset <- abs(min(object2$frequency)) + } + if (offset > max(object2$frequency)) { + offset <- abs(min(object2$frequency)) + } + object2$pos <- ifelse(object2$frequency > 0, + object2$frequency + offset, + object2$frequency - offset) + + # move 0 labels where necessary + object2$pos[object2$frequency == 0] <- ifelse(object2$agreement[object2$frequency == 0] == 1, + object2$pos[object2$frequency == 0] * -1, + object2$pos[object2$frequency == 0]) + object2$label <- as.factor(abs(object2$frequency)) + } else { + object2$count <- object2$frequency + # set frequency of negative qualifiers to negative values + object2$frequency <- ifelse(as.numeric(as.character(object2$agreement)) >= 0, object2$frequency, + object2$frequency * -1) + # remove zero frequencies + object2 <- object2[object2$frequency != 0, ] + # generate position of bar labels + object2$pos <- ifelse(object2$frequency > 0, + 1.1, + -0.1) + # Add labels + object2$label <- paste(object2$count, object2$agreement, sep = " x ") + } + + offset <- (max(object2$frequency) + abs(min(object2$frequency))) * 0.05 + offset <- ifelse(offset < 0.5, 0.5, offset) + yintercepts <- data.frame(x = c(0.5, length(unique(object2$entity)) + 0.5), + y = c(0, 0)) + high <- yintercepts$x[2] + 0.25 + + object2 <- object2[order(as.numeric(as.character(object2$agreement)), + decreasing = FALSE), ] + object2$agreement <- factor(object2$agreement, levels = w) + + # Plot + g <- ggplot2::ggplot(object2, + ggplot2::aes(x = .data[["entity"]], + y = .data[["frequency"]], + fill = .data[["agreement"]], + group = .data[["agreement"]], + label = .data[["label"]])) + if (binary) { # Bars for the binary case + g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], + color = .data[["text_color"]]), + stat = "identity", + width = barWidth, + show.legend = FALSE) + # For the integer case with positive and negative values + } else if (max(w) > 0 & min(w) < 0) { + g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], + color = .data[["text_color"]]), + stat = "identity", + width = barWidth, + show.legend = FALSE, + data = object2[as.numeric(as.character(object2$agreement)) >= 0, ], + position = ggplot2::position_stack(reverse = TRUE)) + + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], + color = .data[["text_color"]]), + stat = "identity", + width = barWidth, + show.legend = FALSE, + data = object2[as.numeric(as.character(object2$agreement)) < 0, ]) + # For the integer case with positive values only + } else if (min(w) >= 0) { + g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], + color = .data[["text_color"]]), + stat = "identity", + width = barWidth, + show.legend = FALSE, + position = ggplot2::position_stack(reverse = TRUE)) + # For the integer case with negative values only + } else { + g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], + color = .data[["text_color"]]), + stat = "identity", + width = barWidth, + show.legend = FALSE) + } + g <- g + ggplot2::coord_flip() + + ggplot2::theme_minimal() + + # Add intercept line + ggplot2::geom_line(ggplot2::aes(x = .data[["x"]], y = .data[["y"]]), + data = yintercepts, + linewidth = axisWidth, + inherit.aes = FALSE) + + # Remove all panel grids, axis titles and axis ticks and text for x-axis + ggplot2::theme(panel.grid.major = ggplot2::element_blank(), + panel.grid.minor = ggplot2::element_blank(), + axis.title = ggplot2::element_blank(), + axis.ticks.y = ggplot2::element_blank(), + axis.text.x = ggplot2::element_blank(), + axis.text.y = ggplot2::element_text(size = fontSize)) #+ + if (is.logical(colors)) { + g <- g + ggplot2::scale_fill_identity() + + ggplot2::scale_color_identity() + } + if (binary) { # Add entity labels for binary case + g <- g + + ggplot2::geom_text(ggplot2::aes(x = .data[["entity"]], + y = .data[["pos"]], + label = .data[["label"]]), + size = (fontSize / ggplot2::.pt), + inherit.aes = FALSE, + data = object2) + # Add entity labels for integer case with positive and negative values + } else if (max(w) > 0 & min(w) < 0) { + g <- g + + ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), + size = (fontSize / ggplot2::.pt), + position = ggplot2::position_stack(vjust = 0.5, reverse = TRUE), + inherit.aes = TRUE, + data = object2[object2$frequency >= 0, ]) + + ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), + size = (fontSize / ggplot2::.pt), + position = ggplot2::position_stack(vjust = 0.5), + inherit.aes = TRUE, + data = object2[object2$frequency < 0, ]) + # Add entity labels for integer case with positive values only + } else if (min(w) >= 0) { + g <- g + + ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), + size = (fontSize / ggplot2::.pt), + position = ggplot2::position_stack(vjust = 0.5, reverse = TRUE), + inherit.aes = TRUE) + } else { + g <- g + + ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), + size = (fontSize / ggplot2::.pt), + position = ggplot2::position_stack(vjust = 0.5), + inherit.aes = TRUE) + } + if (lab) { # Add (dis-)agreement labels + g <- g + + ggplot2::annotate("text", + x = high, + y = offset * 2, + hjust = 0, + label = lab.pos, + size = (fontSize / ggplot2::.pt)) + + ggplot2::annotate("text", + x = high, + y = 0 - offset * 2, + hjust = 1, + label = lab.neg, + size = (fontSize / ggplot2::.pt)) + + # Truncate labels of entities + ggplot2::scale_x_discrete(labels = sapply(as.character(object2$entity), function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e), + expand = c(0, 2, 0, 2), + limits = levels(object2$entity)) + } else { + g <- g + + # Truncate labels of entities + ggplot2::scale_x_discrete(labels = sapply(as.character(object2$entity), function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e), + limits = levels(object2$entity)) + } + return(g) +} \ No newline at end of file diff --git a/rDNA/rDNA/R/dna_multiclust.R b/rDNA/rDNA/R/dna_multiclust.R new file mode 100644 index 00000000..da2e9253 --- /dev/null +++ b/rDNA/rDNA/R/dna_multiclust.R @@ -0,0 +1,1190 @@ +#' Compute multiple cluster solutions for a discourse network +#' +#' Compute multiple cluster solutions for a discourse network. +#' +#' This function applies a number of different graph clustering techniques to +#' a discourse network dataset. The user provides many of the same arguments as +#' in the \code{\link{dna_network}} function and a few additional arguments that +#' determine which kinds of clustering methods should be used and how. In +#' particular, the \code{k} argument can be \code{0} (for arbitrary numbers of +#' clusters) or any positive integer value (e.g., \code{2}, for constraining the +#' number of clusters to exactly \code{k} groups). This is useful for assessing +#' the polarization of a discourse network. +#' +#' In particular, the function can be used to compute the maximal modularity of +#' a smoothed time series of discourse networks using the \code{timeWindow} and +#' \code{windowSize} arguments for a given \code{k} across a number of +#' clustering methods. +#' +#' It is also possible to switch off all but one clustering method using the +#' respective arguments and carry out a simple cluster analysis with the method +#' of choice for a certain time span of the discourse network, without any time +#' window options. +#' +#' @param saveObjects Store the original output of the respective clustering +#' method in the \code{cl} slot of the return object? If \code{TRUE}, one +#' cluster object per time point will be saved, for all time points for which +#' network data are available. At each time point, only the cluster object +#' with the highest modularity score will be saved, all others discarded. The +#' \code{max_mod} slot of the object contains additional information on which +#' measure was saved at each time point and what the corresponding modularity +#' score is. +#' @param k The number of clusters to compute. This constrains the choice of +#' clustering methods because some methods require a predefined \code{k} while +#' other methods do not. To permit arbitrary numbers of clusters, depending on +#' the respective algorithm (or the value of modularity in some cases), choose +#' \code{k = 0}. This corresponds to the theoretical notion of +#' "multipolarization". For "bipolarization", choose \code{k = 2} in order to +#' constrain the cluster solutions to exactly two groups. +#' @param k.max If \code{k = 0}, there can be arbitrary numbers of clusters. In +#' this case, \code{k.max} sets the maximal number of clusters that can be +#' identified. +#' @param single Include hierarchical clustering with single linkage in the pool +#' of clustering methods? The \code{\link[stats]{hclust}} function from +#' the \pkg{stats} package is applied to Jaccard distances in the affiliation +#' network for this purpose. Only valid if \code{k > 1}. +#' @param average Include hierarchical clustering with average linkage in the +#' pool of clustering methods? The \code{\link[stats]{hclust}} function from +#' the \pkg{stats} package is applied to Jaccard distances in the affiliation +#' network for this purpose. Only valid if \code{k > 1}. +#' @param complete Include hierarchical clustering with complete linkage in the +#' pool of clustering methods? The \code{\link[stats]{hclust}} function from +#' the \pkg{stats} package is applied to Jaccard distances in the affiliation +#' network for this purpose. Only valid if \code{k > 1}. +#' @param ward Include hierarchical clustering with Ward's algorithm in the +#' pool of clustering methods? The \code{\link[stats]{hclust}} function from +#' the \pkg{stats} package is applied to Jaccard distances in the affiliation +#' network for this purpose. If \code{k = 0} is selected, different solutions +#' with varying \code{k} are attempted, and the solution with the highest +#' modularity is retained. +#' @param kmeans Include k-means clustering in the pool of clustering methods? +#' The \code{\link[stats]{kmeans}} function from the \pkg{stats} package is +#' applied to Jaccard distances in the affiliation network for this purpose. +#' If \code{k = 0} is selected, different solutions with varying \code{k} are +#' attempted, and the solution with the highest modularity is retained. +#' @param pam Include partitioning around medoids in the pool of clustering +#' methods? The \code{\link[cluster]{pam}} function from the \pkg{cluster} +#' package is applied to Jaccard distances in the affiliation network for this +#' purpose. If \code{k = 0} is selected, different solutions with varying +#' \code{k} are attempted, and the solution with the highest modularity is +#' retained. +#' @param equivalence Include equivalence clustering (as implemented in the +#' \code{\link[sna]{equiv.clust}} function in the \pkg{sna} package), based on +#' shortest path distances between nodes (as implemented in the +#' \code{\link[sna]{sedist}} function in the \pkg{sna} package) in the +#' positive subtract network? If \code{k = 0} is selected, different solutions +#' with varying \code{k} are attempted, and the solution with the highest +#' modularity is retained. +#' @param concor_one Include CONvergence of iterative CORrelations (CONCOR) in +#' the pool of clustering methods? The algorithm is applied to the positive +#' subtract network to identify \code{k = 2} clusters. The method is omitted +#' if \code{k != 2}. +#' @param concor_two Include CONvergence of iterative CORrelations (CONCOR) in +#' the pool of clustering methods? The algorithm is applied to the affiliation +#' network to identify \code{k = 2} clusters. The method is omitted +#' if \code{k != 2}. +#' @param louvain Include the Louvain community detection algorithm in the pool +#' of clustering methods? The \code{\link[igraph]{cluster_louvain}} function +#' in the \pkg{igraph} package is applied to the positive subtract network for +#' this purpose. +#' @param fastgreedy Include the fast and greedy community detection algorithm +#' in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_fast_greedy}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param walktrap Include the Walktrap community detection algorithm +#' in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_walktrap}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param leading_eigen Include the leading eigenvector community detection +#' algorithm in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_leading_eigen}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param edge_betweenness Include the edge betweenness community detection +#' algorithm by Girvan and Newman in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_edge_betweenness}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param infomap Include the infomap community detection algorithm +#' in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_infomap}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param label_prop Include the label propagation community detection algorithm +#' in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_label_prop}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. +#' @param spinglass Include the spinglass community detection algorithm +#' in the pool of clustering methods? The +#' \code{\link[igraph]{cluster_spinglass}} function in the \pkg{igraph} +#' package is applied to the positive subtract network for this purpose. Note +#' that this method is disabled by default because it is relatively slow. +#' @inheritParams dna_network +#' +#' @return The function creates a \code{dna_multiclust} object, which contains +#' the following items: +#' \describe{ +#' \item{k}{The number of clusters determined by the user.} +#' \item{cl}{Cluster objects returned by the respective cluster function. If +#' multiple methods are used, this returns the object with the highest +#' modularity.} +#' \item{max_mod}{A data frame with one row per time point (that is, only one +#' row in the default case and multiple rows if time windows are used) and +#' the maximal modularity for the given time point across all cluster +#' methods.} +#' \item{modularity}{A data frame with the modularity values for all separate +#' cluster methods and all time points.} +#' \item{membership}{A large data frame with all nodes' membership information +#' for each time point and each clustering method.} +#' } +#' +#' @author Philip Leifeld +#' +#' @examples +#' \dontrun{ +#' library("rDNA") +#' dna_init() +#' samp <- dna_sample() +#' dna_openDatabase(samp, coderId = 1, coderPassword = "sample") +#' +#' # example 1: compute 12 cluster solutions for one time point +#' mc1 <- dna_multiclust(variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' duplicates = "document", +#' k = 0, # flexible numbers of clusters +#' saveObjects = TRUE) # retain hclust object +#' +#' mc1$modularity # return modularity scores for 12 clustering methods +#' mc1$max_mod # return the maximal value of the 12, along with dates +#' mc1$memberships # return cluster memberships for all 12 cluster methods +#' plot(mc1$cl[[1]]) # plot hclust dendrogram +#' +#' # example 2: compute only Girvan-Newman edge betweenness with two clusters +#' set.seed(12345) +#' mc2 <- dna_multiclust(k = 2, +#' single = FALSE, +#' average = FALSE, +#' complete = FALSE, +#' ward = FALSE, +#' kmeans = FALSE, +#' pam = FALSE, +#' equivalence = FALSE, +#' concor_one = FALSE, +#' concor_two = FALSE, +#' louvain = FALSE, +#' fastgreedy = FALSE, +#' walktrap = FALSE, +#' leading_eigen = FALSE, +#' edge_betweenness = TRUE, +#' infomap = FALSE, +#' label_prop = FALSE, +#' spinglass = FALSE) +#' mc2$memberships # return membership in two clusters +#' mc2$modularity # return modularity of the cluster solution +#' +#' # example 3: smoothed modularity using time window algorithm +#' mc3 <- dna_multiclust(k = 2, +#' timeWindow = "events", +#' windowSize = 28) +#' mc3$max_mod # maximal modularity and method per time point +#' } +#' +#' @rdname dna_multiclust +#' @importFrom stats as.dist cor hclust cutree kmeans +#' @export +dna_multiclust <- function(statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + duplicates = "include", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + timeWindow = "no", + windowSize = 100, + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE, + saveObjects = FALSE, + k = 0, + k.max = 5, + single = TRUE, + average = TRUE, + complete = TRUE, + ward = TRUE, + kmeans = TRUE, + pam = TRUE, + equivalence = TRUE, + concor_one = TRUE, + concor_two = TRUE, + louvain = TRUE, + fastgreedy = TRUE, + walktrap = TRUE, + leading_eigen = TRUE, + edge_betweenness = TRUE, + infomap = TRUE, + label_prop = TRUE, + spinglass = FALSE) { + + # check dependencies + if (!requireNamespace("igraph", quietly = TRUE)) { # version 0.8.1 required for edge betweenness to work fine. + stop("The 'dna_multiclust' function requires the 'igraph' package to be installed.\n", + "To do this, enter 'install.packages(\"igraph\")'.") + } else if (packageVersion("igraph") < "0.8.1" && edge_betweenness) { + warning("Package version of 'igraph' < 0.8.1. If edge betweenness algorithm encounters an empty network matrix, this will let R crash. See here: https://github.com/igraph/rigraph/issues/336. Consider updating 'igraph' to the latest version.") + } + if (pam && !requireNamespace("cluster", quietly = TRUE)) { + pam <- FALSE + warning("Argument 'pam = TRUE' requires the 'cluster' package, which is not installed.\nSetting 'pam = FALSE'. Consider installing the 'cluster' package.") + } + if (equivalence && !requireNamespace("sna", quietly = TRUE)) { + equivalence <- FALSE + warning("Argument 'equivalence = TRUE' requires the 'sna' package, which is not installed.\nSetting 'equivalence = FALSE'. Consider installing the 'sna' package.") + } + + # check argument validity + if (is.null(k) || is.na(k) || !is.numeric(k) || length(k) > 1 || is.infinite(k) || k < 0) { + stop("'k' must be a non-negative integer number. Can be 0 for flexible numbers of clusters.") + } + if (is.null(k.max) || is.na(k.max) || !is.numeric(k.max) || length(k.max) > 1 || is.infinite(k.max) || k.max < 1) { + stop("'k.max' must be a positive integer number.") + } + if (k == 1) { + k <- 0 + warning("'k' must be 0 (for arbitrary numbers of clusters) or larger than 1 (to constrain number of clusters). Using 'k = 0'.") + } + + # determine what kind of two-mode network to create + if (is.null(qualifier) || is.na(qualifier) || !is.character(qualifier)) { + qualifierAggregation <- "ignore" + } else { + v <- dna_getVariables(statementType = statementType) + if (v$type[v$label == qualifier] == "boolean") { + qualifierAggregation <- "combine" + } else { + qualifierAggregation <- "subtract" + } + } + + nw_aff <- dna_network(networkType = "twomode", + statementType = statementType, + variable1 = variable1, + variable1Document = variable1Document, + variable2 = variable2, + variable2Document = variable2Document, + qualifier = qualifier, + qualifierAggregation = qualifierAggregation, + normalization = "no", + duplicates = duplicates, + start.date = start.date, + stop.date = stop.date, + start.time = start.time, + stop.time = stop.time, + timeWindow = timeWindow, + windowSize = windowSize, + excludeValues = excludeValues, + excludeAuthors = excludeAuthors, + excludeSources = excludeSources, + excludeSections = excludeSections, + excludeTypes = excludeTypes, + invertValues = invertValues, + invertAuthors = invertAuthors, + invertSources = invertSources, + invertSections = invertSections, + invertTypes = invertTypes) + nw_sub <- dna_network(networkType = "onemode", + statementType = statementType, + variable1 = variable1, + variable1Document = variable1Document, + variable2 = variable2, + variable2Document = variable2Document, + qualifier = qualifier, + qualifierAggregation = "subtract", + normalization = "average", + duplicates = duplicates, + start.date = start.date, + stop.date = stop.date, + start.time = start.time, + stop.time = stop.time, + timeWindow = timeWindow, + windowSize = windowSize, + excludeValues = excludeValues, + excludeAuthors = excludeAuthors, + excludeSources = excludeSources, + excludeSections = excludeSections, + excludeTypes = excludeTypes, + invertValues = invertValues, + invertAuthors = invertAuthors, + invertSources = invertSources, + invertSections = invertSections, + invertTypes = invertTypes) + + if (timeWindow == "no") { + dta <- list() + dta$networks <- list(nw_sub) + nw_sub <- dta + dta <- list() + dta$networks <- list(nw_aff) + nw_aff <- dta + } + + obj <- list() + if (isTRUE(saveObjects)) { + obj$cl <- list() + } + dta_dat <- list() + dta_mem <- list() + dta_mod <- list() + counter <- 1 + if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { + num_networks <- length(nw_sub) + } else { + num_networks <- 1 + } + for (i in 1:num_networks) { + + # prepare dates + if (timeWindow == "no") { + dta_dat[[i]] <- data.frame(i = i, + start = attributes(nw_sub$networks[[i]])$start, + stop = attributes(nw_sub$networks[[i]])$stop) + } else { + dta_dat[[i]] <- data.frame(i = i, + start.date = attributes(nw_sub[[i]])$start, + middle.date = attributes(nw_sub[[i]])$middle, + stop.date = attributes(nw_sub[[i]])$stop) + } + + # prepare two-mode network + if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { + x <- nw_aff[[i]] + } else { + x <- nw_aff$networks[[i]] + } + if (qualifierAggregation == "combine") { + combined <- cbind(apply(x, 1:2, function(x) ifelse(x %in% c(1, 3), 1, 0)), + apply(x, 1:2, function(x) ifelse(x %in% c(2, 3), 1, 0))) + } else { + combined <- x + } + combined <- combined[rowSums(combined) > 0, , drop = FALSE] + rn <- rownames(combined) + + # Jaccard distances for two-mode network (could be done using vegdist function in vegan package, but saving the dependency) + combined <- matrix(as.integer(combined > 0), nrow = nrow(combined)) # probably not necessary, but ensure it's an integer matrix + intersections <- tcrossprod(combined) # compute intersections using cross-product + row_sums <- rowSums(combined) # compute row sums + unions <- matrix(outer(row_sums, row_sums, `+`), ncol = length(row_sums)) - intersections # compute unions + jaccard_similarities <- intersections / unions # calculate Jaccard similarities + jaccard_similarities[is.nan(jaccard_similarities)] <- 0 # avoid division by zero + jaccard_distances <- 1 - jaccard_similarities # convert to Jaccard distances + rownames(jaccard_distances) <- rn # re-attach the row names + jac <- stats::as.dist(jaccard_distances) # convert to dist object + + # prepare one-mode network + if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { + y <- nw_sub[[i]] + } else { + y <- nw_sub$networks[[i]] + } + y[y < 0] <- 0 + class(y) <- "matrix" + g <- igraph::graph_from_adjacency_matrix(y, mode = "undirected", weighted = TRUE) + + if (nrow(combined) > 1) { + counter_current <- 1 + current_cl <- list() + current_mod <- numeric() + + # Hierarchical clustering with single linkage + if (isTRUE(single) && k > 1) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "single")) + mem <- stats::cutree(cl, k = k) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Single)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Single)", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with single linkage with optimal k + if (isTRUE(single) && k < 2) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "single")) + opt_k <- lapply(2:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Single)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Single)", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with average linkage + if (isTRUE(average) && k > 1) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "average")) + mem <- stats::cutree(cl, k = k) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Average)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Average)", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with average linkage with optimal k + if (isTRUE(average) && k < 2) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "average")) + opt_k <- lapply(2:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Average)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Average)", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with complete linkage + if (isTRUE(complete) && k > 1) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "complete")) + mem <- stats::cutree(cl, k = k) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Complete)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Complete)", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with complete linkage with optimal k + if (isTRUE(complete) && k < 2) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "complete")) + opt_k <- lapply(2:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Complete)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Complete)", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with the Ward algorithm + if (isTRUE(ward) && k > 1) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "ward.D2")) + mem <- stats::cutree(cl, k = k) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Ward)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Ward)", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Hierarchical clustering with the Ward algorithm with optimal k + if (isTRUE(ward) && k < 2) { + try({ + suppressWarnings(cl <- stats::hclust(jac, method = "ward.D2")) + opt_k <- lapply(2:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Hierarchical (Ward)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Hierarchical (Ward)", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # k-means + if (isTRUE(kmeans) && k > 1) { + try({ + suppressWarnings(cl <- stats::kmeans(jac, centers = k)) + mem <- cl$cluster + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("k-Means", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "k-Means", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # k-means with optimal k + if (isTRUE(kmeans) && k < 2) { + try({ + opt_k <- lapply(2:k.max, function(x) { + suppressWarnings(cl <- stats::kmeans(jac, centers = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("k-Means", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "k-Means", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + cl <- opt_k[[kk]]$cl + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # pam + if (isTRUE(pam) && k > 1) { + try({ + suppressWarnings(cl <- cluster::pam(jac, k = k)) + mem <- cl$cluster + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Partitioning around Medoids", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Partitioning around Medoids", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # pam with optimal k + if (isTRUE(pam) && k < 2) { + try({ + opt_k <- lapply(2:k.max, function(x) { + suppressWarnings(cl <- cluster::pam(jac, k = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Partitioning around Medoids", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Partitioning around Medoids", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + cl <- opt_k[[kk]]$cl + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Equivalence clustering + if (isTRUE(equivalence) && k > 1) { + try({ + suppressWarnings(cl <- sna::equiv.clust(y, equiv.dist = sna::sedist(y, method = "euclidean"))) + mem <- stats::cutree(cl$cluster, k = k) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Equivalence", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Equivalence", + k = k, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Equivalence clustering with optimal k + if (isTRUE(equivalence) && k < 2) { + try({ + suppressWarnings(cl <- sna::equiv.clust(y, equiv.dist = sna::sedist(y, method = "euclidean"))) + opt_k <- lapply(2:k.max, function(x) { + mem <- stats::cutree(cl$cluster, k = x) + mod <- igraph::modularity(x = g, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mod <- max(mod) + mem <- opt_k[[kk]]$mem + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Equivalence", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Equivalence", + k = kk + 1, # add one because the series started with k = 2 + modularity = mod, + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # CONCOR based on the positive subtract network + if (isTRUE(concor_one) && k %in% c(0, 2)) { + try({ + suppressWarnings(mi <- stats::cor(y)) + iter <- 1 + while (any(abs(mi) <= 0.999) & iter <= 50) { + mi[is.na(mi)] <- 0 + mi <- stats::cor(mi) + iter <- iter + 1 + } + mem <- ((mi[, 1] > 0) * 1) + 1 + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("CONCOR (One-Mode)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "CONCOR (One-Mode)", + k = 2, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- mem + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # CONCOR based on the combined affiliation network + if (isTRUE(concor_two) && k %in% c(0, 2)) { + try({ + suppressWarnings(mi <- stats::cor(t(combined))) + iter <- 1 + while (any(abs(mi) <= 0.999) & iter <= 50) { + mi[is.na(mi)] <- 0 + mi <- stats::cor(mi) + iter <- iter + 1 + } + mem <- ((mi[, 1] > 0) * 1) + 1 + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("CONCOR (Two-Mode)", length(mem)), + node = rownames(x), + cluster = mem, + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "CONCOR (Two-Mode)", + k = 2, + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- mem + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Louvain clustering + if (isTRUE(louvain) && k < 2) { + try({ + suppressWarnings(cl <- igraph::cluster_louvain(g)) + mem <- igraph::membership(cl) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Louvain", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Louvain", + k = max(as.numeric(mem)), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Fast & Greedy community detection (with or without cut) + if (isTRUE(fastgreedy)) { + try({ + suppressWarnings(cl <- igraph::cluster_fast_greedy(g, merges = TRUE)) + if (k == 0) { + mem <- igraph::membership(cl) + } else { + mem <- suppressWarnings(igraph::cut_at(cl, no = k)) + if ((k + 1) %in% as.numeric(mem)) { + stop() + } + } + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Fast & Greedy", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Fast & Greedy", + k = ifelse(k == 0, max(as.numeric(mem)), k), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Walktrap community detection (with or without cut) + if (isTRUE(walktrap)) { + try({ + suppressWarnings(cl <- igraph::cluster_walktrap(g, merges = TRUE)) + if (k == 0) { + mem <- igraph::membership(cl) + } else { + mem <- suppressWarnings(igraph::cut_at(cl, no = k)) + if ((k + 1) %in% as.numeric(mem)) { + stop() + } + } + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Walktrap", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Walktrap", + k = ifelse(k == 0, max(as.numeric(mem)), k), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Leading Eigenvector community detection (only without cut) + if (isTRUE(leading_eigen) && k < 2) { # it *should* work with cut_at because is.hierarchical(cl) returns TRUE, but it never works... + try({ + suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) + mem <- igraph::membership(cl) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Leading Eigenvector", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Leading Eigenvector", + k = max(as.numeric(mem)), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Edge Betweenness community detection (with or without cut) + if (isTRUE(edge_betweenness)) { + try({ + suppressWarnings(cl <- igraph::cluster_edge_betweenness(g, merges = TRUE)) + if (k == 0) { + mem <- igraph::membership(cl) + } else { + mem <- suppressWarnings(igraph::cut_at(cl, no = k)) + if ((k + 1) %in% as.numeric(mem)) { + stop() + } + } + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Edge Betweenness", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Edge Betweenness", + k = ifelse(k == 0, max(as.numeric(mem)), k), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Infomap community detection + if (isTRUE(infomap) && k < 2) { + try({ + suppressWarnings(cl <- igraph::cluster_infomap(g)) + mem <- igraph::membership(cl) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Infomap", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Infomap", + k = max(as.numeric(mem)), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Label Propagation community detection + if (isTRUE(label_prop) && k < 2) { + try({ + suppressWarnings(cl <- igraph::cluster_label_prop(g)) + mem <- igraph::membership(cl) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Label Propagation", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Label Propagation", + k = max(as.numeric(mem)), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # Spinglass community detection + if (isTRUE(spinglass) && k < 2) { + try({ + suppressWarnings(cl <- igraph::cluster_spinglass(g)) + mem <- igraph::membership(cl) + dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), + method = rep("Spinglass", length(mem)), + node = rownames(x), + cluster = as.numeric(mem), + stringsAsFactors = FALSE) + dta_mod[[counter]] <- data.frame(i = i, + method = "Spinglass", + k = max(as.numeric(mem)), + modularity = igraph::modularity(x = g, membership = mem), + stringsAsFactors = FALSE) + if (isTRUE(saveObjects)) { + current_cl[[counter_current]] <- cl + current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] + counter_current <- counter_current + 1 + } + counter <- counter + 1 + }, silent = TRUE) + } + + # retain cluster object where modularity was maximal + if (isTRUE(saveObjects) && length(current_cl) > 0) { + obj$cl[[i]] <- current_cl[[which.max(current_mod)]] + } + } + } + obj$cl <- obj$cl[!sapply(obj$cl, is.null)] # remove NULL objects that may occur when the network is empty + obj$k <- k + obj$max_mod <- do.call(rbind, dta_dat) + memberships <- do.call(rbind, dta_mem) + rownames(memberships) <- NULL + obj$memberships <- memberships + obj$modularity <- do.call(rbind, dta_mod) + if (nrow(obj$modularity) == 0) { + stop("No output rows. Either you switched all clustering methods off, or all methods you used produced errors.") + } + obj$max_mod <- obj$max_mod[obj$max_mod$i %in% obj$modularity$i, ] # remove date entries where the network is empty + obj$max_mod$max_mod <- sapply(obj$max_mod$i, function(x) max(obj$modularity$modularity[obj$modularity$i == x], na.rm = TRUE)) # attach max_mod to $max_mod + # attach max_method to $max_mod + obj$max_mod$max_method <- sapply(obj$max_mod$i, + function(x) obj$modularity$method[obj$modularity$i == x & obj$modularity$modularity == max(obj$modularity$modularity[obj$modularity$i == x], na.rm = TRUE)][1]) + # attach k to max_mod + obj$max_mod$k <- sapply(obj$max_mod$i, function(x) max(obj$modularity$k[obj$modularity$i == x], na.rm = TRUE)) + + # diagnostics + if (isTRUE(single) && !"Hierarchical (Single)" %in% obj$modularity$method && k > 1) { + warning("'single' omitted due to an unknown problem.") + } + if (isTRUE(average) && !"Hierarchical (Average)" %in% obj$modularity$method && k > 1) { + warning("'average' omitted due to an unknown problem.") + } + if (isTRUE(complete) && !"Hierarchical (Complete)" %in% obj$modularity$method && k > 1) { + warning("'complete' omitted due to an unknown problem.") + } + if (isTRUE(ward) && !"Hierarchical (Ward)" %in% obj$modularity$method) { + warning("'ward' omitted due to an unknown problem.") + } + if (isTRUE(kmeans) && !"k-Means" %in% obj$modularity$method) { + warning("'kmeans' omitted due to an unknown problem.") + } + if (isTRUE(pam) && !"Partitioning around Medoids" %in% obj$modularity$method) { + warning("'pam' omitted due to an unknown problem.") + } + if (isTRUE(equivalence) && !"Equivalence" %in% obj$modularity$method) { + warning("'equivalence' omitted due to an unknown problem.") + } + if (isTRUE(concor_one) && !"CONCOR (One-Mode)" %in% obj$modularity$method && k %in% c(0, 2)) { + warning("'concor_one' omitted due to an unknown problem.") + } + if (isTRUE(concor_two) && !"CONCOR (Two-Mode)" %in% obj$modularity$method && k %in% c(0, 2)) { + warning("'concor_two' omitted due to an unknown problem.") + } + if (isTRUE(louvain) && !"Louvain" %in% obj$modularity$method && k < 2) { + warning("'louvain' omitted due to an unknown problem.") + } + if (isTRUE(fastgreedy) && !"Fast & Greedy" %in% obj$modularity$method) { + warning("'fastgreedy' omitted due to an unknown problem.") + } + if (isTRUE(walktrap) && !"Walktrap" %in% obj$modularity$method) { + warning("'walktrap' omitted due to an unknown problem.") + } + if (isTRUE(leading_eigen) && !"Leading Eigenvector" %in% obj$modularity$method && k < 2) { + warning("'leading_eigen' omitted due to an unknown problem.") + } + if (isTRUE(edge_betweenness) && !"Edge Betweenness" %in% obj$modularity$method) { + warning("'edge_betweenness' omitted due to an unknown problem.") + } + if (isTRUE(infomap) && !"Infomap" %in% obj$modularity$method && k < 2) { + warning("'infomap' omitted due to an unknown problem.") + } + if (isTRUE(label_prop) && !"Label Propagation" %in% obj$modularity$method && k < 2) { + warning("'label_prop' omitted due to an unknown problem.") + } + if (isTRUE(spinglass) && !"Spinglass" %in% obj$modularity$method && k < 2) { + warning("'spinglass' omitted due to an unknown problem.") + } + + class(obj) <- "dna_multiclust" + return(obj) +} + +#' Print the summary of a \code{dna_multiclust} object +#' +#' Show details of a \code{dna_multiclust} object. +#' +#' Print abbreviated contents for the slots of a \code{dna_multiclust} object, +#' which can be created using the \link{dna_multiclust} function. +#' +#' @param x A \code{dna_multiclust} object. +#' @param ... Further options (currently not used). +#' +#' @author Philip Leifeld +#' +#' @rdname dna_multiclust +#' @importFrom utils head +#' @export +print.dna_multiclust <- function(x, ...) { + cat(paste0("$k\n", x$k, "\n")) + if ("cl" %in% names(x)) { + cat(paste0("\n$cl\n", length(x$cl), " cluster object(s) embedded.\n")) + } + cat("\n$max_mod\n") + print(utils::head(x$max_mod)) + if (nrow(x$max_mod) > 6) { + cat(paste0("[... ", nrow(x$max_mod), " rows]\n")) + } + cat("\n$modularity\n") + print(utils::head(x$modularity)) + if (nrow(x$modularity) > 6) { + cat(paste0("[... ", nrow(x$modularity), " rows]\n")) + } + cat("\n$memberships\n") + print(utils::head(x$memberships)) + if (nrow(x$memberships) > 6) { + cat(paste0("[... ", nrow(x$memberships), " rows]\n")) + } +} \ No newline at end of file diff --git a/rDNA/rDNA/R/dna_network.R b/rDNA/rDNA/R/dna_network.R new file mode 100644 index 00000000..2fbfeac0 --- /dev/null +++ b/rDNA/rDNA/R/dna_network.R @@ -0,0 +1,1166 @@ +#' Compute and retrieve a network +#' +#' Compute and retrieve a network from DNA. +#' +#' This function serves to compute a one-mode or two-mode network or an event +#' list in DNA and retrieve it as a matrix or data frame, respectively. The +#' arguments resemble the export options in DNA. It is also possible to compute +#' a temporal sequence of networks using the moving time window approach, in +#' which case the networks are retrieved as a list of matrices. +#' +#' @param networkType The kind of network to be computed. Can be +#' \code{"twomode"}, \code{"onemode"}, or \code{"eventlist"}. +#' @param statementType The name of the statement type in which the variable +#' of interest is nested. For example, \code{"DNA Statement"}. +#' @param variable1 The first variable for network construction. In a one-mode +#' network, this is the variable for both the rows and columns. In a +#' two-mode network, this is the variable for the rows only. In an event +#' list, this variable is only used to check for duplicates (depending on +#' the setting of the \code{duplicates} argument). +#' @param variable1Document A boolean value indicating whether the first +#' variable is at the document level (i.e., \code{"author"}, +#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or +#' \code{"title"}). +#' @param variable2 The second variable for network construction. In a one-mode +#' network, this is the variable over which the ties are created. For +#' example, if an organization x organization network is created, and ties +#' in this network indicate co-reference to a concept, then the second +#' variable is the \code{"concept"}. In a two-mode network, this is the +#' variable used for the columns of the network matrix. In an event list, +#' this variable is only used to check for duplicates (depending on the +#' setting of the \code{duplicates} argument). +#' @param variable2Document A boolean value indicating whether the second +#' variable is at the document level (i.e., \code{"author"}, +#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or +#' \code{"title"} +#' @param qualifier The qualifier variable. In a one-mode network, this +#' variable can be used to count only congruence or conflict ties. For +#' example, in an organization x organization network via common concepts, +#' a binary \code{"agreement"} qualifier could be used to record only ties +#' where both organizations have a positive stance on the concept or where +#' both organizations have a negative stance on the concept. With an +#' integer qualifier, the tie weight between the organizations would be +#' proportional to the similarity or distance between the two organizations +#' on the scale of the integer variable. With a short text variable as a +#' qualifier, agreement on common categorical values of the qualifier is +#' required, for example a tie is established (or a tie weight increased) if +#' two actors both refer to the same value on the second variable AND match on +#' the categorical qualifier, for example the type of referral. +#' +#' In a two-mode network, the qualifier variable can be used to retain only +#' positive or only negative statements or subtract negative from positive +#' mentions. All of this depends on the setting of the +#' \code{qualifierAggregation} argument. For event lists, the qualifier +#' variable is only used for filtering out duplicates (depending on the +#' setting of the \code{duplicates} argument. +#' +#' The qualifier can also be \code{NULL}, in which case it is ignored, meaning +#' that values in \code{variable1} and \code{variable2} are unconditionally +#' associated with each other in the network when they co-occur. This is +#' identical to selecting a qualifier variable and setting +#' \code{qualifierAggregation = "ignore"}. +#' @param qualifierDocument A boolean value indicating whether the qualifier +#' variable is at the document level (i.e., \code{"author"}, +#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or +#' \code{"title"} +#' @param qualifierAggregation The aggregation rule for the \code{qualifier} +#' variable. In one-mode networks, this must be \code{"ignore"} (for +#' ignoring the qualifier variable), \code{"congruence"} (for recording a +#' network tie only if both nodes have the same qualifier value in the +#' binary case or for recording the similarity between the two nodes on the +#' qualifier variable in the integer case), \code{"conflict"} (for +#' recording a network tie only if both nodes have a different qualifier +#' value in the binary case or for recording the distance between the two +#' nodes on the qualifier variable in the integer case), or +#' \code{"subtract"} (for subtracting the conflict tie value from the +#' congruence tie value in each dyad). In two-mode networks, this must be +#' \code{"ignore"}, \code{"combine"} (for creating multiplex combinations, +#' e.g., 1 for positive, 2 for negative, and 3 for mixed), or +#' \code{subtract} (for subtracting negative from positive ties). In event +#' lists, this setting is ignored. +#' @param normalization Normalization of edge weights. Valid settings for +#' one-mode networks are \code{"no"} (for switching off normalization), +#' \code{"average"} (for average activity normalization), \code{"jaccard"} +#' (for Jaccard coefficient normalization), and \code{"cosine"} (for +#' cosine similarity normalization). Valid settings for two-mode networks +#' are \code{"no"}, \code{"activity"} (for activity normalization), and +#' \code{"prominence"} (for prominence normalization). +#' @param isolates Should all nodes of the respective variable be included in +#' the network matrix (\code{isolates = TRUE}), or should only those nodes +#' be included that are active in the current time period and are not +#' excluded (\code{isolates = FALSE})? +#' @param duplicates Setting for excluding duplicate statements before network +#' construction. Valid settings are \code{"include"} (for including all +#' statements in network construction), \code{"document"} (for counting +#' only one identical statement per document), \code{"week"} (for counting +#' only one identical statement per calendar week), \code{"month"} (for +#' counting only one identical statement per calendar month), \code{"year"} +#' (for counting only one identical statement per calendar year), and +#' \code{"acrossrange"} (for counting only one identical statement across +#' the whole time range). +#' @param start.date The start date for network construction in the format +#' \code{"dd.mm.yyyy"}. All statements before this date will be excluded. +#' @param start.time The start time for network construction on the specified +#' \code{start.date}. All statements before this time on the specified date +#' will be excluded. +#' @param stop.date The stop date for network construction in the format +#' \code{"dd.mm.yyyy"}. All statements after this date will be excluded. +#' @param stop.time The stop time for network construction on the specified +#' \code{stop.date}. All statements after this time on the specified date +#' will be excluded. +#' @param timeWindow Possible values are \code{"no"}, \code{"events"}, +#' \code{"seconds"}, \code{"minutes"}, \code{"hours"}, \code{"days"}, +#' \code{"weeks"}, \code{"months"}, and \code{"years"}. If \code{"no"} is +#' selected (= the default setting), no time window will be used. If any of +#' the time units is selected, a moving time window will be imposed, and +#' only the statements falling within the time period defined by the window +#' will be used to create the network. The time window will then be moved +#' forward by one time unit at a time, and a new network with the new time +#' boundaries will be created. This is repeated until the end of the overall +#' time span is reached. All time windows will be saved as separate +#' networks in a list. The duration of each time window is defined by the +#' \code{windowSize} argument. For example, this could be used to create a +#' time window of 6 months which moves forward by one month each time, thus +#' creating time windows that overlap by five months. If \code{"events"} is +#' used instead of a natural time unit, the time window will comprise +#' exactly as many statements as defined in the \code{windowSize} argument. +#' However, if the start or end statement falls on a date and time where +#' multiple events happen, those additional events that occur simultaneously +#' are included because there is no other way to decide which of the +#' statements should be selected. Therefore the window size is sometimes +#' extended when the start or end point of a time window is ambiguous in +#' event time. +#' @param windowSize The number of time units of which a moving time window is +#' comprised. This can be the number of statement events, the number of days +#' etc., as defined in the \code{"timeWindow"} argument. +#' @param kernel Use kernel smoothing for computing time windows? This option +#' only matters if the \code{timeWindow} argument has a value other than +#' \code{"no"} or \code{"event"}. The default value \code{kernel = "no"} +#' switches off kernel smoothing, which means all statements within a time +#' window are weighted equally. Other values down-weight statements the +#' farther they are temporally away from the mid-point of the time window. +#' Several kernel smoothing functions are available, similar to kernel density +#' estimation: \code{"uniform"} is similar to \code{"no"} and weights all +#' statements with a value of \code{0.5}. \code{"gaussian"} uses a standard +#' normal distribution as a kernel smoother. \code{"epanechnikov"} uses an +#' Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel +#' function. If in doubt, do not use kernel smoothing. +#' @param excludeValues A list of named character vectors that contains entries +#' which should be excluded during network construction. For example, +#' \code{list(concept = c("A", "B"), organization = c("org A", "org B"))} +#' would exclude all statements containing concepts "A" or "B" or +#' organizations "org A" or "org B" when the network is constructed. This +#' is irrespective of whether these values appear in \code{variable1}, +#' \code{variable2}, or the \code{qualifier}. Note that only variables at +#' the statement level can be used here. There are separate arguments for +#' excluding statements nested in documents with certain meta-data. +#' @param excludeAuthors A character vector of authors. If a statement is +#' nested in a document where one of these authors is set in the "Author" +#' meta-data field, the statement is excluded from network construction. +#' @param excludeSources A character vector of sources. If a statement is +#' nested in a document where one of these sources is set in the "Source" +#' meta-data field, the statement is excluded from network construction. +#' @param excludeSections A character vector of sections. If a statement is +#' nested in a document where one of these sections is set in the "Section" +#' meta-data field, the statement is excluded from network construction. +#' @param excludeTypes A character vector of types. If a statement is +#' nested in a document where one of these types is set in the "Type" +#' meta-data field, the statement is excluded from network construction. +#' @param invertValues A boolean value indicating whether the entries provided +#' by the \code{excludeValues} argument should be excluded from network +#' construction (\code{invertValues = FALSE}) or if they should be the only +#' values that should be included during network construction +#' (\code{invertValues = TRUE}). +#' @param invertAuthors A boolean value indicating whether the entries provided +#' by the \code{excludeAuthors} argument should be excluded from network +#' construction (\code{invertAuthors = FALSE}) or if they should be the +#' only values that should be included during network construction +#' (\code{invertAuthors = TRUE}). +#' @param invertSources A boolean value indicating whether the entries provided +#' by the \code{excludeSources} argument should be excluded from network +#' construction (\code{invertSources = FALSE}) or if they should be the +#' only values that should be included during network construction +#' (\code{invertSources = TRUE}). +#' @param invertSections A boolean value indicating whether the entries +#' provided by the \code{excludeSections} argument should be excluded from +#' network construction (\code{invertSections = FALSE}) or if they should +#' be the only values that should be included during network construction +#' (\code{invertSections = TRUE}). +#' @param invertTypes A boolean value indicating whether the entries provided +#' by the \code{excludeTypes} argument should be excluded from network +#' construction (\code{invertTypes = FALSE}) or if they should be the +#' only values that should be included during network construction +#' (\code{invertTypes = TRUE}). +#' @param fileFormat An optional file format specification for saving the +#' resulting network(s) to a file instead of returning an object. Valid values +#' are \code{"csv"} (for network matrices or event lists), \code{"dl"} (for +#' UCINET DL full-matrix files), and \code{"graphml"} (for visone .graphml +#' files). +#' @param outfile An optional output file name for saving the resulting +#' network(s) to a file instead of returning an object. +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' nw <- dna_network(networkType = "onemode", +#' variable1 = "organization", +#' variable2 = "concept", +#' qualifier = "agreement", +#' qualifierAggregation = "congruence", +#' normalization = "average", +#' excludeValues = list("concept" = +#' c("There should be legislation to regulate emissions."))) +#' } +#' +#' @author Philip Leifeld +#' +#' @family networks +#' +#' @importFrom rJava .jarray +#' @importFrom rJava .jcall +#' @importFrom rJava .jnull +#' @importFrom rJava J +#' @export +dna_network <- function(networkType = "twomode", + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "ignore", + normalization = "no", + isolates = FALSE, + duplicates = "include", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + timeWindow = "no", + windowSize = 100, + kernel = "no", + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE, + fileFormat = NULL, + outfile = NULL) { + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + if (is.null(fileFormat)) { + fileFormat <- .jnull(class = "java/lang/String") + } + if (is.null(outfile)) { + outfile <- .jnull(class = "java/lang/String") + } + + # call rNetwork function to compute results + .jcall(dna_getHeadlessDna(), + "V", + "rNetwork", + networkType, + statementType, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + isolates, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + timeWindow, + as.integer(windowSize), + kernel, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes, + outfile, + fileFormat + ) + + exporter <- .jcall(dna_getHeadlessDna(), "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored + + if (networkType == "eventlist") { # assemble an event list in the form of a data frame of filtered statements + f <- J(exporter, "getFilteredStatements", simplify = TRUE) # array list of filtered export statements; use J because array list return type not recognized using .jcall + l <- list() # create a list for filtered statements, later to be converted to data frame, with one row per statement + for (i in seq(.jcall(f, "I", "size")) - 1) { # loop through filtered statements, starting at 0 + fi <- f$get(as.integer(i)) # retrieve filtered statement i + row <- list() # each filtered export statement is represented by a list, with multiple slots for the variables etc. + row$statement_id <- .jcall(fi, "I", "getId") # store the statement ID + row$time <- .jcall(fi, "J", "getDateTimeLong") # store the date/time in seconds since 1 January 1970; will be converted to POSIXct later because the conversion to data frame otherwise converts it back to long anyway + values <- J(fi, "getValues") # array list of variables with values; use J instead of .jcall because array list return type not recognized using .jcall + for (j in seq(.jcall(values, "I", "size")) - 1) { # loop through the variables + vi <- values$get(as.integer(j)) # save variable/value j temporarily to access its contents + dataType <- .jcall(vi, "S", "getDataType") # the data type of value j + if (dataType == "long text") { + row[[.jcall(vi, "S", "getKey")]] <- .jcall(vi, "S", "getValue") # store as character object under variable name if long text + } else if (dataType == "short text") { + row[[.jcall(vi, "S", "getKey")]] <- vi$getValue()$getValue() # extract character object from Entity object and store under variable name if short text + } else { + row[[.jcall(vi, "S", "getKey")]] <- vi$getValue() # store as integer under variable name if boolean or integer data type + } + } + row$start_position <- .jcall(fi, "I", "getStart") # store start caret in document text + row$stop_position <- .jcall(fi, "I", "getStop") # store end caret in document text + row$text <- .jcall(fi, "S", "getText") # text of the statement between start and end caret + row$coder <- .jcall(fi, "I", "getCoderId") # store coder ID; the user can merge this with other coder details like name and color later if needed + row$document_id <- .jcall(fi, "I", "getDocumentId") # store the document ID of the document the statement is contained in + row$document_title <- .jcall(fi, "S", "getTitle") # store the document title + row$document_author <- .jcall(fi, "S", "getAuthor") # store the document author + row$document_source <- .jcall(fi, "S", "getSource") # store the document source + row$document_section <- .jcall(fi, "S", "getSection") # store the document section + row$document_type <- .jcall(fi, "S", "getType") # store the document type + l[[i + 1]] <- row # add the row to the list + } + d <- do.call(rbind.data.frame, l) # convert the list of lists to data frame + d$time <- as.POSIXct(d$time, origin = "1970-01-01 00:00:00") # convert long date/time to POSIXct + return(d) + } else { # assemble a one-mode or two-mode matrix with attributes or a list of matrices (if time window) + m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object + l <- list() # create a list in which each result is stored; can be of length 1 if no time window is used + for (t in 1:length(m)) { # loop through the matrices + mat <- .jcall(m[[t]], "[[D", "getMatrix", simplify = TRUE) # get the resulting matrix at step t as a double[][] object and save as matrix + rownames(mat) <- .jcall(m[[t]], "[S", "getRowNames", simplify = TRUE) # add the row names to the matrix + colnames(mat) <- .jcall(m[[t]], "[S", "getColumnNames", simplify = TRUE) # add the column names to the matrix + attributes(mat)$start <- as.POSIXct(.jcall(m[[t]], "J", "getStartLong"), origin = "1970-01-01") # add the start date/time of the result as an attribute to the matrix + attributes(mat)$stop <- as.POSIXct(.jcall(m[[t]], "J", "getStopLong"), origin = "1970-01-01") # add the end date/time of the result as an attribute to the matrix + if (length(m) > 1) { + attributes(mat)$middle <- as.POSIXct(.jcall(m[[t]], "J", "getDateTimeLong"), origin = "1970-01-01") # add the mid-point date/time around which the time window is centered if the time window algorithm was used + } + attributes(mat)$numStatements <- .jcall(m[[t]], "I", "getNumStatements") # add the number of filtered statements the matrix is based on as an attribute to the matrix + attributes(mat)$call <- match.call() # add the arguments of the call as an attribute to the matrix + class(mat) <- c(paste0("dna_network_", networkType), class(mat)) # add "dna_network_onemode" or "dna_network_twomode" as a class label in addition to "matrix" + l[[t]] <- mat # add the matrix to the list + } + if (length(m) == 1) { + return(l[[1]]) # return the first matrix in the list if no time window was used + } else { + attributes(l)$call <- match.call() # add arguments of the call as an attribute also to the list, not just each network matrix + class(l) <- c(paste0("dna_network_", networkType, "_timewindows"), class(l)) # add "dna_network_onemode_timewindows" or "dna_network_twomode_timewindows" to class label + return(l) # return the list of network matrices + } + } +} + +#' Convert a \code{dna_network_onemode} object to a matrix +#' +#' Convert a \code{dna_network_onemode} object to a matrix. +#' +#' Remove the attributes and \code{"dna_network_onemode"} class label from a +#' \code{dna_network_onemode} object and return it as a numeric matrix. +#' +#' @param x The \code{dna_network_onemode} object, as returned by the +#' \code{\link{dna_network}} function. +#' @param ... Additional arguments. Currently not in use. +#' +#' @author Philip Leifeld +#' +#' @family networks +#' +#' @export +as.matrix.dna_network_onemode <- function(x, ...) { + attr(x, "start") <- NULL + attr(x, "stop") <- NULL + attr(x, "numStatements") <- NULL + attr(x, "call") <- NULL + attr(x, "class") <- NULL + return(x) +} + +#' Convert a \code{dna_network_twomode} object to a matrix +#' +#' Convert a \code{dna_network_twomode} object to a matrix. +#' +#' Remove the attributes and \code{"dna_network_twomode"} class label from a +#' \code{dna_network_twomode} object and return it as a numeric matrix. +#' +#' @param x The \code{dna_network_twomode} object, as returned by the +#' \code{\link{dna_network}} function. +#' @param ... Additional arguments. Currently not in use. +#' +#' @author Philip Leifeld +#' +#' @family networks +#' +#' @export +as.matrix.dna_network_twomode <- as.matrix.dna_network_onemode + +#' Print a \code{dna_network_onemode} object +#' +#' Show details of a \code{dna_network_onemode} object. +#' +#' Print a one-mode network matrix and its attributes. +#' +#' @param x A \code{dna_network_onemode} object, as returned by the +#' \code{\link{dna_network}} function. +#' @param trim Number of maximum characters to display in row and column labels +#' of the matrix. Labels with more characters are truncated, and the last +#' character is replaced by an asterisk (\code{*}). +#' @param attr Display attributes, such as the start and stop date and time, the +#' number of statements on which the matrix is based, the function call and +#' arguments on which the network matrix is based, and the full labels without +#' truncation. +#' @param ... Additional arguments. Currently not in use. +#' +#' @author Philip Leifeld +#' +#' @family networks +#' +#' @export +print.dna_network_onemode <- function(x, trim = 5, attr = TRUE, ...) { + rn <- rownames(x) + cn <- colnames(x) + rownames(x) <- sapply(rownames(x), function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + colnames(x) <- sapply(colnames(x), function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) + x <- round(x, 2) + if ("dna_network_onemode" %in% class(x)) { + onemode <- TRUE + class(x) <- class(x)[class(x) != "dna_network_onemode"] + } else { + onemode <- FALSE + class(x) <- class(x)[class(x) != "dna_network_twomode"] + } + start <- attr(x, "start") + attr(x, "start") <- NULL + stop <- attr(x, "stop") + attr(x, "stop") <- NULL + ns <- attr(x, "numStatements") + attr(x, "numStatements") <- NULL + cl <- deparse(attr(x, "call")) + attr(x, "call") <- NULL + attr(x, "class") <- NULL + print(x) + if (attr) { + cat("\nStart:", as.character(start)) + cat("\nStop: ", as.character(stop)) + cat("\nStatements:", ns) + cat("\nCall:", trimws(cl)) + if (onemode) { + cat("\n\nLabels:\n") + cat(paste(1:length(rn), rn), sep = "\n") + } else { + cat("\n\nRow labels:\n") + cat(paste(1:length(rn), rn), sep = "\n") + cat("\nColumn labels:\n") + cat(paste(1:length(cn), cn), sep = "\n") + } + } +} + +#' Print a \code{dna_network_twomode} object +#' +#' Show details of a \code{dna_network_twomode} object. +#' +#' Print a two-mode network matrix and its attributes. +#' +#' @inheritParams print.dna_network_onemode +#' +#' @author Philip Leifeld +#' +#' @family networks +#' +#' @export +print.dna_network_twomode <- print.dna_network_onemode + +#' Plot networks created using rDNA. +#' +#' Plot a network generated using \code{\link{dna_network}}. +#' +#' These functions plot \code{dna_network_onemode} and +#' \code{dna_network_onemode} objects generated by the \code{\link{dna_network}} +#' function. In order to use this function, please install the \code{igraph} and +#' \code{ggraph} packages. Different layouts for one- and two-mode networks are +#' available. +#' +#' @param object A \code{dna_network} object. +#' @param ... Additional arguments; currently not in use. +#' @param atts A \code{dna_attributes} object generated by +#' \code{\link{dna_getAttributes}}. Provide this object and matching +#' attributes when plotting custom node colors, node labels and/or node sizes. +#' @param layout The type of node layout to use. The following layouts are +#' available from the \code{igraph} and \code{ggraph} packages at the time of +#' writing: +#' \itemize{ +#' \item \code{"stress"} (the default layout) +#' \item \code{"bipartite"} (only for two-mode networks) +#' \item \code{"backbone"} +#' \item \code{"circle"} +#' \item \code{"dh"} +#' \item \code{"drl"} +#' \item \code{"fr"} +#' \item \code{"gem"} +#' \item \code{"graphopt"} +#' \item \code{"kk"} +#' \item \code{"lgl"} +#' \item \code{"mds"} +#' \item \code{"nicely"} +#' \item \code{"randomly"} +#' \item \code{"star"} +#' } +#' See \link[ggraph]{layout_tbl_graph_igraph} for the current list of layouts. +#' @param edge_size_range Two values indicating the minimum and maximum value +#' to scale edge widths. +#' @param edge_color Provide the name of a color for edge colors. The default +#' \code{"NULL"} colors edges in line with the specified +#' \code{qualifierAggregation} in \code{\link{dna_network}}. +#' @param edge_alpha Takes numeric values to control the alpha-transparency of +#' edges. Possible values range from \code{0} (fully transparent) to \code{1} +#' (fully visible). +#' @param node_size Takes positive numeric values to control the size of nodes. +#' Also accepts numeric values matching an attribute of the \code{atts} object +#' (see examples). +#' @param node_colors Provide the name of a color or use an attribute from the +#' \code{atts} object for node colors (see examples). Defaults to +#' \code{"black"}. +#' @param node_label If \code{TRUE}, the row names (in a one-mode network) or +#' the row and column names (in a two-mode network) of the network matrix are +#' used for node labels. Also accepts character objects matching one of the +#' attribute variables of the \code{atts} object (see examples). \code{FALSE} +#' turns off node labels. +#' @param font_size Controls the font size of the node labels. +#' @param truncate Sets the number of characters to which node labels should be +#' truncated. +#' @param threshold Minimum threshold for which edges should be plotted. +#' @param giant_component Only plot the giant component (the biggest connected +#' cluster) of the network. Defaults to \code{FALSE}. +#' @param exclude_isolates Exclude isolates (nodes with no connection to other +#' nodes) from the plot. Defaults to \code{FALSE}. +#' @param max_overlaps Value to exclude node labels that overlap with too many +#' other node labels (see \code{\link[ggrepel]{geom_label_repel}}. Defaults +#' to \code{10}. +#' @param seed Numeric value passed to \link{set.seed}. Ensures that plots are +#' reproducible. +#' +#' @examples +#' \dontrun{ +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' ## one-mode network examples +#' +#' # compute network matrix (subtract + normalization) +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "subtract", +#' normalization = "average") +#' +#' # plot network +#' library("ggplot2") +#' autoplot(nw) +#' +#' # plot only positively weighted edges +#' autoplot(nw, threshold = 0) +#' +#' # congruence network +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "congruence", +#' excludeValues = list("concept" = +#' c("There should be legislation to regulate emissions."))) +#' autoplot(nw) +#' +#' # use entity colors (here: colors of organizations) from attributes +#' atts <- dna_getAttributes(variableId = 2) +#' autoplot(nw, atts = atts, node_colors = "color", layout = "fr") +#' +#' # use colors from attributes (after editing some of them) +#' atts$color[atts$Type == "NGO"] <- "red" # change NGO color to red +#' atts$color[atts$Type == "Government"] <- "blue" # change government to blue +#' autoplot(nw, atts = atts, node_colors = "color") # plot with custom colors +#' +#' # use an attribute, such as type, to plot node labels +#' autoplot(nw, atts = atts, node_label = "Type") +#' +#' # plot node sizes according to the number of statements of entities; +#' # first, compute additional matrix to calculate the number of statements +#' nw_freq <- dna_network(networkType = "twomode", +#' qualifierAggregation = "ignore", +#' normalization = "no") +#' # then add frequency of statements as an attribute +#' atts$freq <- rowSums(nw_freq)[match(atts$value, rownames(nw_freq))] +#' # plot network with node sizes matching statement frequencies +#' autoplot(nw, atts = atts, node_size = "freq", node_colors = "color") +#' +#' # use igraph community detection for identification of network clusters; +#' # remove negative edge weights +#' nw[nw < 0] <- 0 +#' # convert dna_network to igraph object +#' graph <- igraph::graph_from_adjacency_matrix(nw, +#' mode = "undirected", +#' weighted = TRUE, +#' diag = FALSE, +#' add.colnames = NULL, +#' add.rownames = NA) +#' # compute communities using igraph cluster algorithms +#' # (here: fast and greedy as an illustration)) +#' com <- igraph::cluster_fast_greedy(graph) +#' # add node community membership as an attribute +#' atts$membership <- com$membership[match(atts$value, com$names)] +#' # use community membership as node color +#' autoplot(nw, atts = atts, node_colors = "membership") +#' # or plot ellipses using ggforce package +#' library("ggforce") +#' autoplot(nw, atts = atts, node_colors = "color") + +#' geom_mark_ellipse(aes(x = x, +#' y = y, +#' group = com$membership, +#' fill = com$membership), +#' show.legend = FALSE) +#' +#' # add legend to the network plot (here: colors mapped to type attribute) +#' autoplot(nw, atts = atts, node_colors = "color") + +#' scale_color_identity(name = "", +#' labels = c("Government", "NGO", "Business"), +#' guide = "legend") + +#' theme(legend.position = "bottom", # change legend position +#' legend.text = element_text(size = 10)) # change legend font size +#' +#' ## two-mode network examples +#' +#' # compute two-mode network and plot it +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "combine") +#' library("ggplot2") +#' autoplot(nw) +#' +#' # use entity colours (here: colors of organizations); +#' # first, retrieve attributes for first-mode entities (organizations) +#' atts <- dna_getAttributes(variableId = 2) +#' # then, retrieve attributes for second-mode entities (concepts) +#' atts2 <- dna_getAttributes(variableId = 3) +#' # combine both attribute objects +#' atts <- rbind(atts, atts2) +#' # plot the network using the attributes of both variables +#' autoplot(nw, +#' atts = atts, +#' node_colors = "color", +#' layout = "bipartite", +#' max_overlaps = 20) +#' # edit the colors before plotting +#' atts$color[atts$Type == "NGO"] <- "red" # change NGO color to red +#' atts$color[atts$Type == "Government"] <- "blue" # government actors in blue +#' # plot the network with custom colors +#' autoplot(nw, atts = atts, node_colors = "color") +#' +#' # use an attribute, such as type, to plot node labels +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "subtract", +#' normalization = "activity") +#' autoplot(nw, atts = atts, node_label = "Type") +#' +#' # plot node sizes according the number of statements of entities; +#' # first, compute network matrix for plotting +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "subtract", +#' normalization = "activity") +#' # compute dna_attributes objects +#' atts <- dna_getAttributes(variableId = 2) +#' atts2 <- dna_getAttributes(variableId = 3) +#' # compute additional matrix to calculate the number of statements +#' nw_freq <- dna_network(networkType = "twomode", +#' qualifierAggregation = "ignore", +#' normalization = "no") +#' # add frequency of statements as attribute +#' # compute statement frequencies of first-mode entities +#' atts$freq <- rowSums(nw_freq)[match(atts$value, rownames(nw_freq))] +#' # compute statement frequencies of second-mode entities +#' atts2$freq <- colSums(nw_freq)[match(atts2$value, colnames(nw_freq))] +#' # combine both attribute objects +#' atts <- rbind(atts, atts2) +#' # plot network with node sizes matching statement frequencies +#' autoplot(nw, atts = atts, node_size = "freq", node_colors = "color") +#' +#' # use igraph community detection for identification of network clusters +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "subtract", +#' normalization = "activity") +#' # compute dna_attributes objects and combine them +#' atts <- dna_getAttributes(variableId = 2) +#' atts2 <- dna_getAttributes(variableId = 3) +#' atts <- rbind(atts, atts2) +#' # remove negative edge weights +#' nw[nw < 0] <- 0 +#' # convert dna_network to igraph object +#' graph <- igraph::graph_from_incidence_matrix(nw, +#' directed = FALSE, +#' weighted = TRUE, +#' add.names = NULL) +#' # compute communities using igraph cluster algorithms +#' # (here: fast and greedy as an illustration)) +#' com <- igraph::cluster_fast_greedy(graph) +#' # add node community membership as an attribute +#' atts$membership <- com$membership[match(atts$value, com$names)] +#' # use community membership as node color +#' autoplot(nw, atts = atts, node_colors = "membership") +#' # or plot ellipses using ggforce +#' library("ggforce") +#' autoplot(nw, atts = atts, node_colors = "color") + +#' geom_mark_ellipse(aes(x = x, +#' y = y, +#' group = com$membership, +#' fill = com$membership), +#' show.legend = FALSE) +#' } +#' +#' @author Tim Henrichsen +#' +#' @family networks +#' +#' @importFrom ggplot2 autoplot +#' @importFrom ggplot2 aes +#' @importFrom ggplot2 scale_color_identity +#' @importFrom rlang .data +#' @name autoplot.dna_network +NULL + +#' @rdname autoplot.dna_network +#' @export +autoplot.dna_network_onemode <- function(object, + ..., + atts = NULL, + layout = "auto", + edge_size_range = c(0.2, 2), + edge_color = NULL, + edge_alpha = 1, + node_size = 3, + node_colors = "black", + node_label = TRUE, + font_size = 6, + truncate = 50, + threshold = NULL, + giant_component = FALSE, + exclude_isolates = FALSE, + max_overlaps = 10, + seed = 12345) { + set.seed(seed) + + if (!grepl("dna_network", class(object)[1])) { + stop("Invalid data object. Please compute a dna_network object with the ", + "dna_network() function before plotting.") + } + + if (!requireNamespace("igraph", quietly = TRUE)) { + stop("The autoplot function requires the 'igraph' package to be installed.\n", + "To do this, enter 'install.packages(\"igraph\")'.") + } + + if (!requireNamespace("ggraph", quietly = TRUE)) { + stop("The autoplot function requires the 'ggraph' package to be installed.\n", + "To do this, enter 'install.packages(\"ggraph\")'.") + } + + if (!is.null(atts) & !"dna_attributes" %in% class(atts)) { + stop("Object provided in 'atts' is not a dna_attributes object. Please ", + "provide a dna_attributes object using dna_getAttributes() or set atts ", + "to NULL if you do not want to use DNA attributes.") + } + + if (!is.numeric(truncate)) { + truncate <- Inf + warning("No numeric value provided for trimming of entities. Truncation ", + "will be ignored.") + } + + # Convert network matrix to igraph network + if ("dna_network_onemode" %in% class(object)) { + graph <- igraph::graph_from_adjacency_matrix(object, + mode = "undirected", + weighted = TRUE, + diag = FALSE, + add.colnames = NULL, + add.rownames = NA) + igraph::V(graph)$shape <- "circle" + } else if ("dna_network_twomode" %in% class(object)) { + graph <- igraph::graph_from_incidence_matrix(object, + directed = FALSE, + weighted = TRUE, + add.names = NULL) + igraph::V(graph)$shape <- ifelse(igraph::V(graph)$type, "square", "circle") + } + + # Check if all entities are included in attributes object (if provided) + if (!is.null(atts) & !(all(igraph::V(graph)$name %in% atts$value))) { + miss <- which(!igraph::V(graph)$name %in% atts$value) + stop("Some network entities are missing in the attributes object:\n", + paste(igraph::V(graph)$name[miss], collapse = "\n")) + } + + # Remove tie weights below threshold + if (!is.null(threshold)) { + graph <- igraph::delete_edges(graph, which(!igraph::E(graph)$weight >= threshold)) + } + + # Add node colors + if (is.character(node_colors)) { + if (!is.null(atts) & length(node_colors) == 1 && node_colors %in% colnames(atts)) { + col_pos <- which(colnames(atts) == node_colors) + igraph::V(graph)$color <- atts[match(igraph::V(graph)$name, atts$value), col_pos] + } else if (length(node_colors) > 1 & length(node_colors) != igraph::vcount(graph)) { + stop("Number of custom colors does not equal number of nodes in the network.") + } else { + igraph::V(graph)$color <- node_colors + } + } else { + igraph::V(graph)$color <- "black" + } + + # Add edge colors + if (is.null(edge_color)) { + if ("combine" %in% as.character(attributes(object)$call)) { + igraph::E(graph)$color <- "green" + igraph::E(graph)$color[igraph::E(graph)$weight == 2] <- "red" + igraph::E(graph)$color[igraph::E(graph)$weight == 3] <- "blue" + # Change edge weight for networks with combine aggregation + igraph::E(graph)$weight[igraph::E(graph)$weight > 0] <- 1 + } else if ("subtract" %in% as.character(attributes(object)$call)) { + igraph::E(graph)$color <- "green" + igraph::E(graph)$color[igraph::E(graph)$weight < 0] <- "red" + } else if ("congruence" %in% as.character(attributes(object)$call)) { + igraph::E(graph)$color <- "green" + } else if ("conflict" %in% as.character(attributes(object)$call)) { + igraph::E(graph)$color <- "red" + } else { + igraph::E(graph)$color <- "gray" + } + } else if (!all(is.na(edge_color))) { + if (length(edge_color) > 1 & length(edge_color) != igraph::ecount(graph)) { + igraph::E(graph)$color <- "gray" + warning("Number of custom edge_colors does not match number of edges ", + "in the network. Will set edge_color to default (gray).") + } else { + igraph::E(graph)$color <- edge_color + } + } else { + igraph::E(graph)$color <- "gray" + } + + # Add node size(s) + if (length(node_size) > 1 & length(node_size) != igraph::vcount(graph)) { + igraph::V(graph)$size <- 7 + warning("Number of provided node size values does not equal number of ", + "nodes in the network. node_size will be set to default value (7).") + } else if (is.character(node_size) & length(node_size) == 1 & !is.null(atts) && node_size %in% colnames(atts)) { + col_pos <- which(colnames(atts) == node_size) + igraph::V(graph)$size <- atts[match(igraph::V(graph)$name, atts$value), col_pos] + } else if (is.numeric(node_size)) { + igraph::V(graph)$size <- node_size + } + + # Add labels + if (!is.logical(node_label)) { + if (is.character(node_label) & length(node_label) == 1 & !is.null(atts) && node_label %in% colnames(atts)) { + col_pos <- which(colnames(atts) == node_label) + igraph::V(graph)$name <- atts[match(igraph::V(graph)$name, atts$value), col_pos] + } else if (!is.null(node_label)) { + if (length(node_label) > 1 & length(node_label) != igraph::vcount(graph)) { + stop("Number of custom labels does not equal number of nodes in the network.") + } + igraph::V(graph)$name <- node_label + } + } + + # Remove isolates + if (exclude_isolates) { + graph <- igraph::delete_vertices(graph, igraph::degree(graph) == 0) + } + + # Only plot giant component of network. Useful for some plotting algorithms. + if (giant_component) { + # Get giant component + components <- igraph::clusters(graph) + biggest_cluster_id <- which.max(components$csize) + + # Get members of giant component + vert_ids <- igraph::V(graph)[components$membership == biggest_cluster_id] + + # Create subgraph + graph <- igraph::induced_subgraph(graph, vert_ids) + } + + + # Truncate labels of entities + igraph::V(graph)$name <- sapply(igraph::V(graph)$name, function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e) + + # Use absolute edge weight values for plotting + igraph::E(graph)$weight <- abs(igraph::E(graph)$weight) + + # Start network plot + g <- ggraph::ggraph(graph, layout = layout, ...) + + suppressWarnings(ggraph::geom_edge_link(ggplot2::aes(edge_width = igraph::E(graph)$weight, edge_colour = igraph::E(graph)$color), + alpha = edge_alpha, + show.legend = FALSE)) + # add edges + ggraph::scale_edge_width(range = edge_size_range) + # add edge scale + ggraph::geom_node_point(ggplot2::aes(colour = igraph::V(graph)$color), # add nodes + size = igraph::V(graph)$size, + shape = igraph::V(graph)$shape, + show.legend = NA) + # Add labels + if ((!is.null(node_label) && !all(is.na(node_label))) && (is.character(node_label) || node_label == TRUE)) { + g <- g + + ggraph::geom_node_text(ggplot2::aes(label = igraph::V(graph)$name), + repel = TRUE, + max.overlaps = max_overlaps, + show.legend = FALSE) + } + + # Add theme and set node colors and edges to identity + g <- g + + ggraph::theme_graph(base_family = "", base_size = font_size) + + ggplot2::scale_color_identity() + + ggraph::scale_edge_color_identity() + + return(g) +} + +#' @rdname autoplot.dna_network +#' @export +autoplot.dna_network_twomode <- autoplot.dna_network_onemode + +#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object +#' +#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object. +#' +#' Convert a \code{dna_network_onemode} or \code{dna_network_twomode} object +#' into a \code{tbl_graph} object as defined in the tidygraph package. These +#' objects can then be plotted using the ggraph package, which contains many +#' network layouts. +#' +#' \code{tbl_graph} objects are an extension of \code{graph} +#' objects defined in the igraph package. Functions for manipulating or plotting +#' the resulting objects from either the tidygraph or igraph package or both +#' can be used. +#' +#' The resulting objects can also be converted to \code{network} objects as +#' defined in the network package (part of the statnet suite of packages) using +#' the \code{asNetwork} function in the intergraph package. +#' +#' @param network A \code{dna_network_onemode} or \code{dna_network_twomode} +#' object to be converted into a \code{tbl_graph} object. Can also be a matrix +#' with edge weights and row and column names for the node labels. +#' @param attributes A \code{dna_attributes} object created using the +#' \link{dna_getAttributes} function with attributes for the nodes in the +#' network. Can also be a data frame with a \code{values} column that contains +#' the node labels and further columns containing the attributes. The +#' attributes are saved as node attributes in the \code{tbl_graph} object. If +#' \code{NULL}, no attributes are included. +#' @param ... Further arguments. Currently not in use. +#' +#' @examples +#' \dontrun{ +#' # prepare toy data +#' dna_sample() +#' dna_openDatabase("sample.dna", coderPassword = "sample") +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "congruence", +#' excludeValues = list(concept = +#' "There should be legislation to regulate emissions.")) +#' at <- dna_getAttributes(variableId = 2) +#' +#' # convert to tbl_graph object +#' g <- dna_tidygraph(nw, at) +#' +#' # basic visualization +#' ggraph::ggraph(g, layout = "fr") + +#' ggraph::geom_edge_link() + +#' ggraph::geom_node_point() +#' +#' # visualization with more bells and whistles +#' ggraph::ggraph(g, layout = "graphopt") + +#' ggraph::geom_edge_link(ggplot2::aes(color = weight, width = weight)) + +#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::scale_edge_color_gradient(low = "azure2", high = "azure4") + +#' ggraph::theme_graph(background = "white") + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) +#' # for more layouts, see vignette("Layouts", package = "ggraph") +#' +#' # hive plot example +#' g <- g |> +#' tidygraph::activate(nodes) |> +#' tidygraph::mutate(centrality = tidygraph::centrality_betweenness()) +#' ggraph::ggraph(g, layout = "hive", axis = Type, sort.by = centrality) + +#' ggraph::geom_edge_hive(ggplot2::aes(colour = "gray", width = weight)) + +#' ggraph::geom_axis_hive(ggplot2::aes(colour = color), +#' size = 5, +#' label = TRUE) + +#' ggraph::scale_edge_color_identity() + +#' theme(legend.position = "none") +#' +#' # example with negative edge weights +#' nw <- dna_network(networkType = "onemode", +#' qualifierAggregation = "subtract", +#' excludeValues = list(concept = +#' "There should be legislation to regulate emissions.")) +#' g <- dna_tidygraph(nw, at) +#' ggraph::ggraph(g, layout = "linear", circular = TRUE) + +#' ggraph::geom_edge_arc(aes(color = color, width = abs)) + +#' ggraph::scale_edge_color_identity() + +#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::theme_graph(background = "white") + +#' theme(legend.position = "none") + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) +#' +#' # example with a two-mode network +#' nw <- dna_network(networkType = "twomode", +#' qualifierAggregation = "combine") +#' at1 <- dna_getAttributes(statementTypeId = 1, variable = "organization") +#' at2 <- dna_getAttributes(statementTypeId = 1, variable = "concept") +#' at1$Notes <- "organization" +#' at2$Notes <- "concept" +#' at <- rbind(at1, at2) +#' g <- dna_tidygraph(nw, at) +#' ggraph::ggraph(g, layout = "graphopt") + +#' ggraph::geom_edge_link(ggplot2::aes(color = color), width = 1) + +#' ggraph::scale_edge_color_identity() + +#' ggraph::geom_node_point(ggplot2::aes(color = color, shape = Notes), +#' size = 5) + +#' ggplot2::scale_color_identity() + +#' ggraph::geom_node_text(ggplot2::aes(label = name), +#' repel = TRUE, +#' max.overlaps = 10, +#' show.legend = FALSE) + +#' ggraph::theme_graph(background = "white") + +#' theme(legend.position = "none") +#' +#' # manipulate and plot using the igraph package +#' library("igraph") +#' class(g) # resulting objects are both tbl_graph and igraph objects +#' igraph::V(g) # get the nodes using igraph functions +#' igraph::E(g) # get the edges using igraph functions +#' igraph::plot(g) # plot network using igraph package +#' +#' # convert to network object (network package, statnet suite of packages) +#' library("intergraph") +#' intergraph::asNetwork(g) +#' } +#' +#' @author Philip Leifeld +#' @family networks +#' @importFrom rlang .data +#' @export +dna_tidygraph <- function(network, attributes = NULL, ...) { + if (length(intersect(c("dna_network_onemode", "dna_network_twomode", "matrix"), class(network))) < 1) { + stop("The 'network' argument must provide an object created by the 'dna_network' function or a matrix.") + } + if (!is.null(attributes) && (length(intersect(c("dna_attributes", "data.frame"), class(attributes))) < 1) || !"value" %in% colnames(attributes)) { + stop("The 'attributes' argument must be NULL or created by the 'dna_getAttributes' function or a data frame with a 'values' column.") + } + if (!requireNamespace("tidygraph", quietly = TRUE) || packageVersion("tidygraph") < "1.3.1") { + stop("The 'dna_tidygraph' function requires the 'tidygraph' package (>= 1.3.1) to be installed.\n", + "To do this, enter 'install.packages(\"tidygraph\")'.") + } + + if ("dna_network_twomode" %in% class(network)) { + nodes <- data.frame(name = c(rownames(network), colnames(network)), type = c(rep(TRUE, nrow(network)), rep(FALSE, ncol(network))), stringsAsFactors = FALSE) + edges <- data.frame(from = rep(rownames(network), times = ncol(network)), to = rep(colnames(network), each = nrow(network)), weight = as.vector(network)) + edges <- edges[edges$weight != 0, ] + edges$from <- match(edges$from, nodes$name) + edges$to <- match(edges$to, nodes$name) + g <- tidygraph::tbl_graph(nodes = nodes, edges = edges, directed = FALSE) # create tbl_graph object for ggraph + } else if ("dna_network_onemode" %in% class(network)) { + g <- tidygraph::as_tbl_graph(network, directed = FALSE) # create tbl_graph object for ggraph + } else { + stop("Argument supplied by argument 'network' not recognized.") + } + + if (!is.null(attributes)) { + nodes <- tidygraph::as_tibble(g, active = "nodes")$name # extract nodes from graph for matching + at <- attributes[attributes$value %in% nodes, ] # retain only those attributes present in the network + at <- at[match(nodes, at$value), ] # sort attributes in the same order as the nodes in the graph + g <- tidygraph::mutate(g, at[, colnames(at) != "value"]) # embed node attributes in graph + } + + edges <- tidygraph::as_tibble(g, active = "edges") # extract edges from graph + u <- unique(edges$weight) # unique edge weights + combined <- length(u) < 5 && any(grepl("combine", attributes(network)$call)) # combined qualifier aggregation? + edgecol <- sapply(edges$weight, function(weight) { # create edge colors + if (length(u) == 2 & all(sort(u) %in% 0:1) & weight > 0) { # binary: 1 = gray + "gray" + } else if (combined) { # "combined" qualifier aggregation + if (weight == 1) { + "green" + } else if (weight == 2) { + "red" + } else if (weight == 3) { + "blue" + } else { + "gray" + } + } else if (any(u < 0)) { # "subtract" (or something else that generates negative ties) + if (weight < 0) { + "red" + } else { + "green" + } + } else { # any other scale, for example "congruence" qualifier aggregation + "gray" + } + }) + g <- g |> # assign absolute values, edge colors, and sign as edge attributes + tidygraph::activate(edges) |> + tidygraph::mutate(abs = abs(.data$weight), + color = .data$edgecol, + sign = ifelse(.data$weight < 0, "negative", "positive")) + + return(g) +} \ No newline at end of file diff --git a/rDNA/rDNA/R/dna_phaseTransitions.R b/rDNA/rDNA/R/dna_phaseTransitions.R new file mode 100644 index 00000000..6984bc5d --- /dev/null +++ b/rDNA/rDNA/R/dna_phaseTransitions.R @@ -0,0 +1,643 @@ +#' Detect phase transitions and states in a discourse network +#' +#' Detect phase transitions and states in a discourse network. +#' +#' This function applies the state dynamics methods of Masuda and Holme to a +#' time window discourse network. It computes temporally overlapping discourse +#' networks, computes the dissimilarity between all networks, and clusters them. +#' For the dissimilarity, the sum of absolute edge weight differences and the +#' Euclidean spectral distance are available. Several clustering techniques can +#' be applied to identify the different stages and phases from the resulting +#' distance matrix. +#' +#' The function offers kernel smoothing, which means the farther away from a +#' time point a statement is, the less important it becomes for the network that +#' is created around the time point. Several kernel smoothing functions are +#' available; see the \code{kernel} argument. +#' +#' @param distanceMethod The distance measure that expresses the dissimilarity +#' between any two network matrices. The following choices are available: +#' \itemize{ +#' \item \code{"absdiff"}: The sum of the cell-wise absolute differences +#' between the two matrices, i.e., the sum of differences in edge weights. +#' This is equivalent to the graph edit distance because the network +#' dimensions are kept constant across all networks by including all nodes +#' at all time points (i.e., by including isolates). +#' \item \code{"spectral"}: The Euclidean distance between the normalized +#' eigenvalues of the graph Laplacian matrices, also called the spectral +#' distance between two network matrices. Any negative values (e.g., from +#' the subtract method) are replaced by zero before computing the +#' distance. +#' } +#' @param clusterMethods The clustering techniques that are applied to the +#' distance matrix in the end. Hierarchical methods are repeatedly cut off at +#' different levels, and solutions are compared using network modularity to +#' pick the best-fitting cluster membership vector. Some of the methods are +#' slower than others, hence they are not included by default. It is possible +#' to include any number of methods in the argument. For each included method, +#' the cluster membership vector (i.e., the states over time) along with the +#' associated time stamps of the networks are returned, and the modularity of +#' each included method is computed for comparison. The following methods are +#' available: +#' \itemize{ +#' \item \code{"single"}: Hierarchical clustering with single linkage using +#' the \code{\link[stats]{hclust}} function from the \pkg{stats} package. +#' \item \code{"average"}: Hierarchical clustering with average linkage +#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} +#' package. +#' \item \code{"complete"}: Hierarchical clustering with complete linkage +#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} +#' package. +#' \item \code{"ward"}: Hierarchical clustering with Ward's method (D2) +#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} +#' package. +#' \item \code{"kmeans"}: k-means clustering using the +#' \code{\link[stats]{kmeans}} function from the \pkg{stats} package. +#' \item \code{"pam"}: Partitioning around medoids using the +#' \code{\link[cluster]{pam}} function from the \pkg{cluster} package. +#' \item \code{"spectral"}: Spectral clustering. An affinity matrix using a +#' Gaussian (RBF) kernel is created. The Laplacian matrix of the affinity +#' matrix is computed and normalized. The first first k eigenvectors of +#' the normalized Laplacian matrix are clustered using k-means. +#' \item \code{"concor"}: CONvergence of iterative CORrelations (CONCOR) +#' with exactly \code{k = 2} clusters. (Not included by default because of +#' the limit to \code{k = 2}.) +#' \item \code{"fastgreedy"}: Fast & greedy community detection using the +#' \code{\link[igraph]{cluster_fast_greedy}} function in the \pkg{igraph} +#' package. +#' \item \code{"walktrap"}: Walktrap community detection using the +#' \code{\link[igraph]{cluster_walktrap}} function in the \pkg{igraph} +#' package. +#' \item \code{"leading_eigen"}: Leading eigenvector community detection +#' using the \code{\link[igraph]{cluster_leading_eigen}} function in the +#' \pkg{igraph} package. (Can be slow, hence not included by default.) +#' \item \code{"edge_betweenness"}: Girvan-Newman edge betweenness community +#' detection using the \code{\link[igraph]{cluster_edge_betweenness}} +#' function in the \pkg{igraph} package. (Can be slow, hence not included +#' by default.) +#' } +#' @param k.min For the hierarchical cluster methods, how many clusters or +#' states should at least be identified? Only the best solution between +#' \code{k.min} and \code{k.max} clusters is retained and compared to other +#' methods. +#' @param k.max For the hierarchical cluster methods, up to how many clusters or +#' states should be identified? Only the best solution between \code{k.min} +#' and \code{k.max} clusters is retained and compared to other methods. +#' @param cores The number of computing cores for parallel processing. If +#' \code{1} (the default), no parallel processing is used. If a larger number, +#' the \pkg{pbmcapply} package is used to parallelize the clustering. Note +#' that this method is based on forking and is only available on Unix +#' operating systems, including MacOS and Linux. Note also that the remaining +#' computations, including the computation of the distance matrix and the +#' time window network generation with kernel smoothing, are done in parallel +#' using threads in Java, irrespective of this setting, using as many parallel +#' threads as cores are available on the system. +#' @param kernel Use kernel smoothing for computing network time slices? Several +#' kernel smoothing functions are available, similar to kernel density +#' estimation. They down-weight statements the farther they are temporally +#' away from the temporal mid-point of the respective time slice. Valid +#' settings are: +#' \itemize{ +#' \item \code{"uniform"}: Weight all statements within a time window +#' equally with a value of \code{0.5}. +#' \item \code{"triangular"}: Use a triangular kernel function. +#' \item \code{"epanechnikov"}: Use an Epanechnikov kernel smoother. +#' \item \code{"gaussian"}: Use a standard normal distribution as a kernel +#' smoother. +#' \item \code{"no"}: Circumvent kernel smoothing and weight all statements +#' with a value of \code{1.0}. This is a legacy setting and is slow and +#' may not return the same results as \code{"uniform"} due to the way it +#' was written up. +#' } +#' @param normalizeToOne Divide all cells by the sum of all cells before +#' computing the dissimilarity between two network matrices? This +#' normalization scales all edge weights to a sum of \code{1.0}. Doing so can +#' make networks more comparable by boosting the edge weights of networks that +#' are relatively sparsely populated by concepts, for example at the beginning +#' or end of the debate. Note that this normalization should not make any +#' difference with Euclidean spectral distances of the graph Laplacian because +#' the eigenvalues are normalized to sum to one in this distance method. +#' @param indentTime If \code{TRUE}, the sequence of time slices under the time +#' window algorithm starts with the first network and ends with the last +#' network that are entirely covered within the timeline defined by the start +#' and stop dates and times. For example, if the start date is 1 February, the +#' stop date is 31 December, and the time window duration is 21 days, the +#' mid-point of the first time window will be 11 February (to ensure the first +#' network entirely fits into the timeline), and the last network will be +#' centered around 20 December (to ensure the last network entirely fits into +#' the timeline). If \code{FALSE}, the start and stop dates and times are used +#' as the first and last mid-points. In that case, the first and last few +#' networks may contain fewer statements than other time slices and may, +#' therefore, be more similar to each other. This can potentially be +#' counter-acted by setting the \code{normalizeToOne} argument. +#' @inheritParams dna_network +#' +#' @examples +#' \dontrun{ +#' library("ggplot2") +#' dna_init() +#' dna_sample() +#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") +#' +#' # compute states and phases for sample dataset +#' results <- dna_phaseTransitions(distanceMethod = "spectral", +#' clusterMethods = c("ward", +#' "pam", +#' "concor", +#' "walktrap"), +#' k.min = 2, +#' k.max = 6, +#' networkType = "onemode", +#' variable1 = "organization", +#' variable2 = "concept", +#' timeWindow = "days", +#' windowSize = 15, +#' kernel = "gaussian", +#' indentTime = FALSE, +#' normalizeToOne = FALSE) +#' results +#' autoplot(results) +#' +#' # access individual plots +#' plots <- autoplot(results) +#' plots[[1]] # show heatmap +#' plots[[2]] # show cluster silhouettes +#' plots[[3]] # show temporal embedding +#' plots[[4]] # show state dynamics +#' +#' # save plots to combined PDF +#' library("ggplotify") # needed to convert heatmap to ggplot diagram +#' library("patchwork") # needed to merge plots into 4 x 4 diagram +#' p1 <- ggplotify::as.ggplot(plots[[1]]) +#' p <- p1 + plots[[2]] + plots[[3]] + plots[[4]] + plot_layout(ncol = 2) +#' ggsave(filename = "phase_transitions.pdf", p, width = 14, height = 12) +#' } +#' +#' @rdname dna_phaseTransitions +#' @author Philip Leifeld +#' @importFrom stats dist +#' @importFrom utils combn +#' @importFrom rJava .jarray .jcall .jnull J +#' @export +dna_phaseTransitions <- function(distanceMethod = "absdiff", + clusterMethods = c("single", + "average", + "complete", + "ward", + "kmeans", + "pam", + "spectral", + "fastgreedy", + "walktrap"), + k.min = 2, + k.max = 6, + cores = 1, + networkType = "twomode", + statementType = "DNA Statement", + variable1 = "organization", + variable1Document = FALSE, + variable2 = "concept", + variable2Document = FALSE, + qualifier = "agreement", + qualifierDocument = FALSE, + qualifierAggregation = "subtract", + normalization = "no", + duplicates = "document", + start.date = "01.01.1900", + stop.date = "31.12.2099", + start.time = "00:00:00", + stop.time = "23:59:59", + timeWindow = "days", + windowSize = 200, + kernel = "uniform", + normalizeToOne = TRUE, + indentTime = FALSE, + excludeValues = list(), + excludeAuthors = character(), + excludeSources = character(), + excludeSections = character(), + excludeTypes = character(), + invertValues = FALSE, + invertAuthors = FALSE, + invertSources = FALSE, + invertSections = FALSE, + invertTypes = FALSE) { + + # check arguments and packages + if (distanceMethod == "spectral" && networkType == "twomode") { + distanceMethod <- "absdiff" + warning("Spectral distances only work with one-mode networks. Using 'distanceMethod = \"absdiff\"' instead.") + } + if (cores > 1 && !requireNamespace("pbmcapply", quietly = TRUE)) { + pbmclapply <- FALSE + warning("Argument 'cores' requires the 'pbmcapply' package, which is not installed.\nSetting 'cores = 1'. Consider installing the 'pbmcapply' package if you use Linux or MacOS.") + } + igraphMethods <- c("louvain", "fastgreedy", "walktrap", "leading_eigen", "edge_betweenness", "infomap", "label_prop", "spinglass") + if (any(igraphMethods %in% clusterMethods) && !requireNamespace("igraph", quietly = TRUE)) { + clusterMethods <- clusterMethods[-igraphMethods] + warning("'igraph' package not installed. Dropping clustering methods from the 'igraph' package. Consider installing 'igraph'.") + } + if ("pam" %in% clusterMethods && !requireNamespace("cluster", quietly = TRUE)) { + clusterMethods <- clusterMethods[which(clusterMethods != "pam")] + warning("'cluster' package not installed. Dropping clustering methods from the 'cluster' package. Consider installing 'cluster'.") + } + if ("concor" %in% clusterMethods && k.min > 2) { + clusterMethods <- clusterMethods[which(clusterMethods != "concor")] + warning("Dropping 'concor' from clustering methods because the CONCOR implementation in rDNA can only find exactly two clusters, but the 'k.min' argument was larger than 2.") + } + clusterMethods <- rev(clusterMethods) # reverse order to save time during parallel computation by starting the computationally intensive methods first + mcall <- match.call() # save the arguments for storing them in the results later + + # generate the time window networks + if (is.null(timeWindow) || is.na(timeWindow) || !is.character(timeWindow) || length(timeWindow) != 1 || !timeWindow %in% c("events", "seconds", "minutes", "hours", "days", "weeks", "months", "years")) { + timeWindow <- "events" + warning("The 'timeWindow' argument was invalid. Proceeding with 'timeWindow = \"events\" instead.") + } + + # wrap the vectors of exclude values for document variables into Java arrays + excludeAuthors <- .jarray(excludeAuthors) + excludeSources <- .jarray(excludeSources) + excludeSections <- .jarray(excludeSections) + excludeTypes <- .jarray(excludeTypes) + + # compile exclude variables and values vectors + dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) + count <- 0 + if (length(excludeValues) > 0) { + for (i in 1:length(excludeValues)) { + if (length(excludeValues[[i]]) > 0) { + for (j in 1:length(excludeValues[[i]])) { + count <- count + 1 + dat[count, 1] <- names(excludeValues)[i] + dat[count, 2] <- excludeValues[[i]][j] + } + } + } + var <- dat[, 1] + val <- dat[, 2] + } else { + var <- character() + val <- character() + } + var <- .jarray(var) # array of variable names of each excluded value + val <- .jarray(val) # array of values to be excluded + + # encode R NULL as Java null value if necessary + if (is.null(qualifier) || is.na(qualifier)) { + qualifier <- .jnull(class = "java/lang/String") + } + + # call rNetwork function to compute results + .jcall(dna_getHeadlessDna(), + "V", + "rTimeWindow", + networkType, + statementType, + variable1, + variable1Document, + variable2, + variable2Document, + qualifier, + qualifierDocument, + qualifierAggregation, + normalization, + TRUE, + duplicates, + start.date, + stop.date, + start.time, + stop.time, + timeWindow, + as.integer(windowSize), + kernel, + normalizeToOne, + indentTime, + var, + val, + excludeAuthors, + excludeSources, + excludeSections, + excludeTypes, + invertValues, + invertAuthors, + invertSources, + invertSections, + invertTypes + ) + exporter <- dna_getHeadlessDna()$getExporter() # save Java object reference to exporter class + + # compute distance matrix + if (distanceMethod == "modularity") { + stop("Differences in modularity have not been implemented yet. Please use absolute differences or spectral Euclidean distance as a distance method.") + } else if (!distanceMethod %in% c("absdiff", "spectral")) { + stop("Distance method not recognized. Try \"absdiff\" or \"spectral\".") + } + distance_mat <- .jcall(exporter, + "[[D", + "computeDistanceMatrix", + distanceMethod, + simplify = TRUE) + distance_mat <- distance_mat / max(distance_mat) # rescale between 0 and 1 + + # retrieve mid-point dates (gamma) + m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object + dates <- sapply(m, function(x) .jcall(x, "J", "getDateTimeLong")) # long integers, still needs conversion to date + + # define clustering function + hclustMethods <- c("single", "average", "complete", "ward") + cl <- function(method, distmat) { + tryCatch({ + similarity_mat <- 1 - distmat + g <- igraph::graph_from_adjacency_matrix(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance + if (method %in% hclustMethods) { + if (method == "single") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "single")) + } else if (method == "average") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "average")) + } else if (method == "complete") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "complete")) + } else if (method == "ward") { + suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "ward.D2")) + } + opt_k <- lapply(k.min:k.max, function(x) { + mem <- stats::cutree(cl, k = x) + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "kmeans") { + opt_k <- lapply(k.min:k.max, function(x) { + suppressWarnings(cl <- stats::kmeans(distmat, centers = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "pam") { + opt_k <- lapply(k.min:k.max, function(x) { + suppressWarnings(cl <- cluster::pam(distmat, k = x)) + mem <- cl$cluster + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(cl = cl, mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "spectral") { + sigma <- 1.0 + affinity_matrix <- exp(-distmat^2 / (2 * sigma^2)) + L <- diag(rowSums(affinity_matrix)) - affinity_matrix + D.sqrt.inv <- diag(1 / sqrt(rowSums(affinity_matrix))) + L.norm <- D.sqrt.inv %*% L %*% D.sqrt.inv + eigenvalues <- eigen(L.norm) # eigenvalue decomposition + opt_k <- lapply(k.min:k.max, function(x) { + U <- eigenvalues$vectors[, 1:x] + mem <- kmeans(U, centers = x)$cluster # cluster the eigenvectors + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } else if (method == "concor") { + suppressWarnings(mi <- stats::cor(similarity_mat)) + iter <- 1 + while (any(abs(mi) <= 0.999) & iter <= 50) { + mi[is.na(mi)] <- 0 + mi <- stats::cor(mi) + iter <- iter + 1 + } + mem <- ((mi[, 1] > 0) * 1) + 1 + } else if (method %in% igraphMethods) { + if (method == "fastgreedy") { + suppressWarnings(cl <- igraph::cluster_fast_greedy(g)) + } else if (method == "walktrap") { + suppressWarnings(cl <- igraph::cluster_walktrap(g)) + } else if (method == "leading_eigen") { + suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) + } else if (method == "edge_betweenness") { + suppressWarnings(cl <- igraph::cluster_edge_betweenness(g)) + } else if (method == "spinglass") { + suppressWarnings(cl <- igraph::cluster_spinglass(g)) + } + opt_k <- lapply(k.min:k.max, function(x) { + mem <- igraph::cut_at(communities = cl, no = x) + mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) + return(list(mem = mem, mod = mod)) + }) + mod <- sapply(opt_k, function(x) x$mod) + kk <- which.max(mod) + mem <- opt_k[[kk]]$mem + } + list(method = method, + modularity = igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem), + memberships = mem) + }, + error = function(e) { + warning("Cluster method '", method, "' could not be computed due to an error: ", e) + }, + warning = function(w) { + warning("Cluster method '", method, "' threw a warning: ", w) + }) + } + + # apply all clustering methods to distance matrix + if (cores > 1) { + cat(paste("Clustering distance matrix on", cores, "cores.\n")) + a <- Sys.time() + l <- pbmcapply::pbmclapply(clusterMethods, cl, distmat = distance_mat, mc.cores = cores) + b <- Sys.time() + } else { + cat("Clustering distance matrix... ") + a <- Sys.time() + l <- lapply(clusterMethods, cl, distmat = distance_mat) + b <- Sys.time() + cat(intToUtf8(0x2714), "\n") + } + print(b - a) + for (i in length(l):1) { + if (length(l[[i]]) == 1) { + l <- l[-i] + clusterMethods <- clusterMethods[-i] + } + } + results <- list() + mod <- sapply(l, function(x) x$modularity) + best <- which(mod == max(mod))[1] + results$modularity <- mod[best] + results$clusterMethod <- clusterMethods[best] + + # temporal embedding via MDS + if (!requireNamespace("MASS", quietly = TRUE)) { + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), + "state" = l[[best]]$memberships) + results$states <- mem + warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") + } else { + cat("Temporal embedding...\n") + a <- Sys.time() + distmat <- distance_mat + 1e-12 + mds <- MASS::isoMDS(distmat) # MDS of distance matrix + points <- mds$points + mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), + "state" = l[[best]]$memberships, + "X1" = points[, 1], + "X2" = points[, 2]) + results$states <- mem + b <- Sys.time() + print(b - a) + } + + results$distmat <- distance_mat + class(results) <- "dna_phaseTransitions" + attributes(results)$stress <- ifelse(ncol(results$states) == 2, NA, mds$stress) + attributes(results)$call <- mcall + return(results) +} + +#' Print the summary of a \code{dna_phaseTransitions} object +#' +#' Show details of a \code{dna_phaseTransitions} object. +#' +#' Print a summary of a \code{dna_phaseTransitions} object, which can be created +#' using the \link{dna_phaseTransitions} function. +#' +#' @param x A \code{dna_phaseTransitions} object. +#' @param ... Further options (currently not used). +#' +#' @author Philip Leifeld +#' +#' @rdname dna_phaseTransitions +#' @importFrom utils head +#' @export +print.dna_phaseTransitions <- function(x, ...) { + cat(paste0("States: ", max(x$states$state), ". Cluster method: ", x$clusterMethod, ". Modularity: ", round(x$modularity, 3), ".\n\n")) + print(utils::head(x$states, 20)) + cat(paste0("...", nrow(x$states), " further rows\n")) +} + +#' @rdname dna_phaseTransitions +#' @param object A \code{"dna_phaseTransitions"} object. +#' @param ... Additional arguments. Currently not in use. +#' @param plots The plots to include in the output list. Can be one or more of +#' the following: \code{"heatmap"}, \code{"silhouette"}, \code{"mds"}, +#' \code{"states"}. +#' +#' @author Philip Leifeld, Kristijan Garic +#' @importFrom ggplot2 autoplot ggplot aes geom_line geom_point xlab ylab +#' labs ggtitle theme_bw theme arrow unit scale_shape_manual element_text +#' scale_x_datetime scale_colour_manual guides +#' @importFrom rlang .data +#' @export +autoplot.dna_phaseTransitions <- function(object, ..., plots = c("heatmap", "silhouette", "mds", "states")) { + # settings for all plots + k <- max(object$states$state) + shapes <- c(21:25, 0:14)[1:k] + l <- list() + + # heatmap + if ("heatmap" %in% plots) { + try({ + if (!requireNamespace("heatmaply", quietly = TRUE)) { + warning("Heatmap skipped because the 'heatmaply' package is not installed.") + } else { + l[[length(l) + 1]] <- heatmaply::ggheatmap(1 - object$distmat, + dendrogram = "both", + showticklabels = FALSE, # remove axis labels + show_dendrogram = TRUE, + hide_colorbar = TRUE) + } + }) + } + + # silhouette plot + if ("silhouette" %in% plots) { + try({ + if (!requireNamespace("cluster", quietly = TRUE)) { + warning("Silhouette plot skipped because the 'cluster' package is not installed.") + } else if (!requireNamespace("factoextra", quietly = TRUE)) { + warning("Silhouette plot skipped because the 'factoextra' package is not installed.") + } else { + sil <- cluster::silhouette(object$states$state, dist(object$distmat)) + l[[length(l) + 1]] <- factoextra::fviz_silhouette(sil, print.summary = FALSE) + + ggplot2::ggtitle(paste0("Cluster silhouettes (mean width: ", round(mean(sil[, 3]), 3), ")")) + + ggplot2::ylab("Silhouette width") + + ggplot2::labs(fill = "State", color = "State") + + ggplot2::theme_classic() + + ggplot2::theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) + } + }) + } + + # temporal embedding + if ("mds" %in% plots) { + try({ + if (is.na(attributes(object)$stress)) { + warning("No temporal embedding found. Skipping this plot.") + } else if (!requireNamespace("igraph", quietly = TRUE)) { + warning("Temporal embedding plot skipped because the 'igraph' package is not installed.") + } else if (!requireNamespace("ggraph", quietly = TRUE)) { + warning("Temporal embedding plot skipped because the 'ggraph' package is not installed.") + } else { + nodes <- object$states + nodes$date <- as.character(nodes$date) + nodes$State <- as.factor(nodes$state) + + # Extract state values + state_values <- nodes$State + + edges <- data.frame(sender = as.character(object$states$date), + receiver = c(as.character(object$states$date[2:(nrow(object$states))]), "NA")) + edges <- edges[-nrow(edges), ] + g <- igraph::graph_from_data_frame(edges, directed = TRUE, vertices = nodes) + l[[length(l) + 1]] <- ggraph::ggraph(g, layout = "manual", x = igraph::V(g)$X1, y = igraph::V(g)$X2) + + ggraph::geom_edge_link(arrow = ggplot2::arrow(type = "closed", length = ggplot2::unit(2, "mm")), + start_cap = ggraph::circle(1, "mm"), + end_cap = ggraph::circle(2, "mm")) + + ggraph::geom_node_point(ggplot2::aes(shape = state_values, fill = state_values), size = 2) + + ggplot2::scale_shape_manual(values = shapes) + + ggplot2::ggtitle("Temporal embedding (MDS)") + + ggplot2::xlab("Dimension 1") + + ggplot2::ylab("Dimension 2") + + ggplot2::theme_bw() + + ggplot2::guides(size = "none") + + ggplot2::labs(shape = "State", fill = "State") + } + }) + } + + # state dynamics + if ("states" %in% plots) { + try({ + d <- data.frame( + time = object$states$date, + id = cumsum(c(TRUE, diff(object$states$state) != 0)), + State = factor(object$states$state, levels = 1:k, labels = paste("State", 1:k)), + time1 = as.Date(object$states$date) + ) + + # Extracting values + time_values <- d$time + state_values <- d$State + id_values <- d$id + + l[[length(l) + 1]] <- ggplot2::ggplot(d, ggplot2::aes(x = time_values, y = state_values, colour = state_values)) + + ggplot2::geom_line(aes(group = 1), linewidth = 2, color = "black", lineend = "square") + + ggplot2::geom_line(aes(group = id_values), linewidth = 2, lineend = "square") + + ggplot2::scale_x_datetime(date_labels = "%b %Y", breaks = "4 months") + # format x-axis as month year + ggplot2::xlab("Time") + + ggplot2::ylab("") + + ggplot2::ggtitle("State dynamics") + + ggplot2::theme_bw() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + ggplot2::guides(linewidth = "none") + + ggplot2::labs(color = "State") + }) + } + + return(l) +} \ No newline at end of file diff --git a/rDNA/rDNA/R/rDNA.R b/rDNA/rDNA/R/rDNA.R index 7a93ee6a..288124d4 100644 --- a/rDNA/rDNA/R/rDNA.R +++ b/rDNA/rDNA/R/rDNA.R @@ -796,4312 +796,4 @@ dna_getAttributes <- function(statementType = NULL, colnames(dat) <- varNames class(dat) <- c("dna_attributes", class(dat)) return(dat) -} - - -# Networks --------------------------------------------------------------------- - -#' Compute and retrieve a network -#' -#' Compute and retrieve a network from DNA. -#' -#' This function serves to compute a one-mode or two-mode network or an event -#' list in DNA and retrieve it as a matrix or data frame, respectively. The -#' arguments resemble the export options in DNA. It is also possible to compute -#' a temporal sequence of networks using the moving time window approach, in -#' which case the networks are retrieved as a list of matrices. -#' -#' @param networkType The kind of network to be computed. Can be -#' \code{"twomode"}, \code{"onemode"}, or \code{"eventlist"}. -#' @param statementType The name of the statement type in which the variable -#' of interest is nested. For example, \code{"DNA Statement"}. -#' @param variable1 The first variable for network construction. In a one-mode -#' network, this is the variable for both the rows and columns. In a -#' two-mode network, this is the variable for the rows only. In an event -#' list, this variable is only used to check for duplicates (depending on -#' the setting of the \code{duplicates} argument). -#' @param variable1Document A boolean value indicating whether the first -#' variable is at the document level (i.e., \code{"author"}, -#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or -#' \code{"title"}). -#' @param variable2 The second variable for network construction. In a one-mode -#' network, this is the variable over which the ties are created. For -#' example, if an organization x organization network is created, and ties -#' in this network indicate co-reference to a concept, then the second -#' variable is the \code{"concept"}. In a two-mode network, this is the -#' variable used for the columns of the network matrix. In an event list, -#' this variable is only used to check for duplicates (depending on the -#' setting of the \code{duplicates} argument). -#' @param variable2Document A boolean value indicating whether the second -#' variable is at the document level (i.e., \code{"author"}, -#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or -#' \code{"title"} -#' @param qualifier The qualifier variable. In a one-mode network, this -#' variable can be used to count only congruence or conflict ties. For -#' example, in an organization x organization network via common concepts, -#' a binary \code{"agreement"} qualifier could be used to record only ties -#' where both organizations have a positive stance on the concept or where -#' both organizations have a negative stance on the concept. With an -#' integer qualifier, the tie weight between the organizations would be -#' proportional to the similarity or distance between the two organizations -#' on the scale of the integer variable. With a short text variable as a -#' qualifier, agreement on common categorical values of the qualifier is -#' required, for example a tie is established (or a tie weight increased) if -#' two actors both refer to the same value on the second variable AND match on -#' the categorical qualifier, for example the type of referral. -#' -#' In a two-mode network, the qualifier variable can be used to retain only -#' positive or only negative statements or subtract negative from positive -#' mentions. All of this depends on the setting of the -#' \code{qualifierAggregation} argument. For event lists, the qualifier -#' variable is only used for filtering out duplicates (depending on the -#' setting of the \code{duplicates} argument. -#' -#' The qualifier can also be \code{NULL}, in which case it is ignored, meaning -#' that values in \code{variable1} and \code{variable2} are unconditionally -#' associated with each other in the network when they co-occur. This is -#' identical to selecting a qualifier variable and setting -#' \code{qualifierAggregation = "ignore"}. -#' @param qualifierDocument A boolean value indicating whether the qualifier -#' variable is at the document level (i.e., \code{"author"}, -#' \code{"source"}, \code{"section"}, \code{"type"}, \code{"id"}, or -#' \code{"title"} -#' @param qualifierAggregation The aggregation rule for the \code{qualifier} -#' variable. In one-mode networks, this must be \code{"ignore"} (for -#' ignoring the qualifier variable), \code{"congruence"} (for recording a -#' network tie only if both nodes have the same qualifier value in the -#' binary case or for recording the similarity between the two nodes on the -#' qualifier variable in the integer case), \code{"conflict"} (for -#' recording a network tie only if both nodes have a different qualifier -#' value in the binary case or for recording the distance between the two -#' nodes on the qualifier variable in the integer case), or -#' \code{"subtract"} (for subtracting the conflict tie value from the -#' congruence tie value in each dyad). In two-mode networks, this must be -#' \code{"ignore"}, \code{"combine"} (for creating multiplex combinations, -#' e.g., 1 for positive, 2 for negative, and 3 for mixed), or -#' \code{subtract} (for subtracting negative from positive ties). In event -#' lists, this setting is ignored. -#' @param normalization Normalization of edge weights. Valid settings for -#' one-mode networks are \code{"no"} (for switching off normalization), -#' \code{"average"} (for average activity normalization), \code{"jaccard"} -#' (for Jaccard coefficient normalization), and \code{"cosine"} (for -#' cosine similarity normalization). Valid settings for two-mode networks -#' are \code{"no"}, \code{"activity"} (for activity normalization), and -#' \code{"prominence"} (for prominence normalization). -#' @param isolates Should all nodes of the respective variable be included in -#' the network matrix (\code{isolates = TRUE}), or should only those nodes -#' be included that are active in the current time period and are not -#' excluded (\code{isolates = FALSE})? -#' @param duplicates Setting for excluding duplicate statements before network -#' construction. Valid settings are \code{"include"} (for including all -#' statements in network construction), \code{"document"} (for counting -#' only one identical statement per document), \code{"week"} (for counting -#' only one identical statement per calendar week), \code{"month"} (for -#' counting only one identical statement per calendar month), \code{"year"} -#' (for counting only one identical statement per calendar year), and -#' \code{"acrossrange"} (for counting only one identical statement across -#' the whole time range). -#' @param start.date The start date for network construction in the format -#' \code{"dd.mm.yyyy"}. All statements before this date will be excluded. -#' @param start.time The start time for network construction on the specified -#' \code{start.date}. All statements before this time on the specified date -#' will be excluded. -#' @param stop.date The stop date for network construction in the format -#' \code{"dd.mm.yyyy"}. All statements after this date will be excluded. -#' @param stop.time The stop time for network construction on the specified -#' \code{stop.date}. All statements after this time on the specified date -#' will be excluded. -#' @param timeWindow Possible values are \code{"no"}, \code{"events"}, -#' \code{"seconds"}, \code{"minutes"}, \code{"hours"}, \code{"days"}, -#' \code{"weeks"}, \code{"months"}, and \code{"years"}. If \code{"no"} is -#' selected (= the default setting), no time window will be used. If any of -#' the time units is selected, a moving time window will be imposed, and -#' only the statements falling within the time period defined by the window -#' will be used to create the network. The time window will then be moved -#' forward by one time unit at a time, and a new network with the new time -#' boundaries will be created. This is repeated until the end of the overall -#' time span is reached. All time windows will be saved as separate -#' networks in a list. The duration of each time window is defined by the -#' \code{windowSize} argument. For example, this could be used to create a -#' time window of 6 months which moves forward by one month each time, thus -#' creating time windows that overlap by five months. If \code{"events"} is -#' used instead of a natural time unit, the time window will comprise -#' exactly as many statements as defined in the \code{windowSize} argument. -#' However, if the start or end statement falls on a date and time where -#' multiple events happen, those additional events that occur simultaneously -#' are included because there is no other way to decide which of the -#' statements should be selected. Therefore the window size is sometimes -#' extended when the start or end point of a time window is ambiguous in -#' event time. -#' @param windowSize The number of time units of which a moving time window is -#' comprised. This can be the number of statement events, the number of days -#' etc., as defined in the \code{"timeWindow"} argument. -#' @param kernel Use kernel smoothing for computing time windows? This option -#' only matters if the \code{timeWindow} argument has a value other than -#' \code{"no"} or \code{"event"}. The default value \code{kernel = "no"} -#' switches off kernel smoothing, which means all statements within a time -#' window are weighted equally. Other values down-weight statements the -#' farther they are temporally away from the mid-point of the time window. -#' Several kernel smoothing functions are available, similar to kernel density -#' estimation: \code{"uniform"} is similar to \code{"no"} and weights all -#' statements with a value of \code{0.5}. \code{"gaussian"} uses a standard -#' normal distribution as a kernel smoother. \code{"epanechnikov"} uses an -#' Epanechnikov kernel smoother. \code{"triangular"} uses a triangular kernel -#' function. If in doubt, do not use kernel smoothing. -#' @param excludeValues A list of named character vectors that contains entries -#' which should be excluded during network construction. For example, -#' \code{list(concept = c("A", "B"), organization = c("org A", "org B"))} -#' would exclude all statements containing concepts "A" or "B" or -#' organizations "org A" or "org B" when the network is constructed. This -#' is irrespective of whether these values appear in \code{variable1}, -#' \code{variable2}, or the \code{qualifier}. Note that only variables at -#' the statement level can be used here. There are separate arguments for -#' excluding statements nested in documents with certain meta-data. -#' @param excludeAuthors A character vector of authors. If a statement is -#' nested in a document where one of these authors is set in the "Author" -#' meta-data field, the statement is excluded from network construction. -#' @param excludeSources A character vector of sources. If a statement is -#' nested in a document where one of these sources is set in the "Source" -#' meta-data field, the statement is excluded from network construction. -#' @param excludeSections A character vector of sections. If a statement is -#' nested in a document where one of these sections is set in the "Section" -#' meta-data field, the statement is excluded from network construction. -#' @param excludeTypes A character vector of types. If a statement is -#' nested in a document where one of these types is set in the "Type" -#' meta-data field, the statement is excluded from network construction. -#' @param invertValues A boolean value indicating whether the entries provided -#' by the \code{excludeValues} argument should be excluded from network -#' construction (\code{invertValues = FALSE}) or if they should be the only -#' values that should be included during network construction -#' (\code{invertValues = TRUE}). -#' @param invertAuthors A boolean value indicating whether the entries provided -#' by the \code{excludeAuthors} argument should be excluded from network -#' construction (\code{invertAuthors = FALSE}) or if they should be the -#' only values that should be included during network construction -#' (\code{invertAuthors = TRUE}). -#' @param invertSources A boolean value indicating whether the entries provided -#' by the \code{excludeSources} argument should be excluded from network -#' construction (\code{invertSources = FALSE}) or if they should be the -#' only values that should be included during network construction -#' (\code{invertSources = TRUE}). -#' @param invertSections A boolean value indicating whether the entries -#' provided by the \code{excludeSections} argument should be excluded from -#' network construction (\code{invertSections = FALSE}) or if they should -#' be the only values that should be included during network construction -#' (\code{invertSections = TRUE}). -#' @param invertTypes A boolean value indicating whether the entries provided -#' by the \code{excludeTypes} argument should be excluded from network -#' construction (\code{invertTypes = FALSE}) or if they should be the -#' only values that should be included during network construction -#' (\code{invertTypes = TRUE}). -#' @param fileFormat An optional file format specification for saving the -#' resulting network(s) to a file instead of returning an object. Valid values -#' are \code{"csv"} (for network matrices or event lists), \code{"dl"} (for -#' UCINET DL full-matrix files), and \code{"graphml"} (for visone .graphml -#' files). -#' @param outfile An optional output file name for saving the resulting -#' network(s) to a file instead of returning an object. -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' nw <- dna_network(networkType = "onemode", -#' variable1 = "organization", -#' variable2 = "concept", -#' qualifier = "agreement", -#' qualifierAggregation = "congruence", -#' normalization = "average", -#' excludeValues = list("concept" = -#' c("There should be legislation to regulate emissions."))) -#' } -#' -#' @author Philip Leifeld -#' -#' @family networks -#' -#' @importFrom rJava .jarray -#' @importFrom rJava .jcall -#' @importFrom rJava .jnull -#' @importFrom rJava J -#' @export -dna_network <- function(networkType = "twomode", - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "ignore", - normalization = "no", - isolates = FALSE, - duplicates = "include", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - timeWindow = "no", - windowSize = 100, - kernel = "no", - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE, - fileFormat = NULL, - outfile = NULL) { - - # wrap the vectors of exclude values for document variables into Java arrays - excludeAuthors <- .jarray(excludeAuthors) - excludeSources <- .jarray(excludeSources) - excludeSections <- .jarray(excludeSections) - excludeTypes <- .jarray(excludeTypes) - - # compile exclude variables and values vectors - dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) - count <- 0 - if (length(excludeValues) > 0) { - for (i in 1:length(excludeValues)) { - if (length(excludeValues[[i]]) > 0) { - for (j in 1:length(excludeValues[[i]])) { - count <- count + 1 - dat[count, 1] <- names(excludeValues)[i] - dat[count, 2] <- excludeValues[[i]][j] - } - } - } - var <- dat[, 1] - val <- dat[, 2] - } else { - var <- character() - val <- character() - } - var <- .jarray(var) # array of variable names of each excluded value - val <- .jarray(val) # array of values to be excluded - - # encode R NULL as Java null value if necessary - if (is.null(qualifier) || is.na(qualifier)) { - qualifier <- .jnull(class = "java/lang/String") - } - if (is.null(fileFormat)) { - fileFormat <- .jnull(class = "java/lang/String") - } - if (is.null(outfile)) { - outfile <- .jnull(class = "java/lang/String") - } - - # call rNetwork function to compute results - .jcall(dna_getHeadlessDna(), - "V", - "rNetwork", - networkType, - statementType, - variable1, - variable1Document, - variable2, - variable2Document, - qualifier, - qualifierDocument, - qualifierAggregation, - normalization, - isolates, - duplicates, - start.date, - stop.date, - start.time, - stop.time, - timeWindow, - as.integer(windowSize), - kernel, - var, - val, - excludeAuthors, - excludeSources, - excludeSections, - excludeTypes, - invertValues, - invertAuthors, - invertSources, - invertSections, - invertTypes, - outfile, - fileFormat - ) - - exporter <- .jcall(dna_getHeadlessDna(), "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored - - if (networkType == "eventlist") { # assemble an event list in the form of a data frame of filtered statements - f <- J(exporter, "getFilteredStatements", simplify = TRUE) # array list of filtered export statements; use J because array list return type not recognized using .jcall - l <- list() # create a list for filtered statements, later to be converted to data frame, with one row per statement - for (i in seq(.jcall(f, "I", "size")) - 1) { # loop through filtered statements, starting at 0 - fi <- f$get(as.integer(i)) # retrieve filtered statement i - row <- list() # each filtered export statement is represented by a list, with multiple slots for the variables etc. - row$statement_id <- .jcall(fi, "I", "getId") # store the statement ID - row$time <- .jcall(fi, "J", "getDateTimeLong") # store the date/time in seconds since 1 January 1970; will be converted to POSIXct later because the conversion to data frame otherwise converts it back to long anyway - values <- J(fi, "getValues") # array list of variables with values; use J instead of .jcall because array list return type not recognized using .jcall - for (j in seq(.jcall(values, "I", "size")) - 1) { # loop through the variables - vi <- values$get(as.integer(j)) # save variable/value j temporarily to access its contents - dataType <- .jcall(vi, "S", "getDataType") # the data type of value j - if (dataType == "long text") { - row[[.jcall(vi, "S", "getKey")]] <- .jcall(vi, "S", "getValue") # store as character object under variable name if long text - } else if (dataType == "short text") { - row[[.jcall(vi, "S", "getKey")]] <- vi$getValue()$getValue() # extract character object from Entity object and store under variable name if short text - } else { - row[[.jcall(vi, "S", "getKey")]] <- vi$getValue() # store as integer under variable name if boolean or integer data type - } - } - row$start_position <- .jcall(fi, "I", "getStart") # store start caret in document text - row$stop_position <- .jcall(fi, "I", "getStop") # store end caret in document text - row$text <- .jcall(fi, "S", "getText") # text of the statement between start and end caret - row$coder <- .jcall(fi, "I", "getCoderId") # store coder ID; the user can merge this with other coder details like name and color later if needed - row$document_id <- .jcall(fi, "I", "getDocumentId") # store the document ID of the document the statement is contained in - row$document_title <- .jcall(fi, "S", "getTitle") # store the document title - row$document_author <- .jcall(fi, "S", "getAuthor") # store the document author - row$document_source <- .jcall(fi, "S", "getSource") # store the document source - row$document_section <- .jcall(fi, "S", "getSection") # store the document section - row$document_type <- .jcall(fi, "S", "getType") # store the document type - l[[i + 1]] <- row # add the row to the list - } - d <- do.call(rbind.data.frame, l) # convert the list of lists to data frame - d$time <- as.POSIXct(d$time, origin = "1970-01-01 00:00:00") # convert long date/time to POSIXct - return(d) - } else { # assemble a one-mode or two-mode matrix with attributes or a list of matrices (if time window) - m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object - l <- list() # create a list in which each result is stored; can be of length 1 if no time window is used - for (t in 1:length(m)) { # loop through the matrices - mat <- .jcall(m[[t]], "[[D", "getMatrix", simplify = TRUE) # get the resulting matrix at step t as a double[][] object and save as matrix - rownames(mat) <- .jcall(m[[t]], "[S", "getRowNames", simplify = TRUE) # add the row names to the matrix - colnames(mat) <- .jcall(m[[t]], "[S", "getColumnNames", simplify = TRUE) # add the column names to the matrix - attributes(mat)$start <- as.POSIXct(.jcall(m[[t]], "J", "getStartLong"), origin = "1970-01-01") # add the start date/time of the result as an attribute to the matrix - attributes(mat)$stop <- as.POSIXct(.jcall(m[[t]], "J", "getStopLong"), origin = "1970-01-01") # add the end date/time of the result as an attribute to the matrix - if (length(m) > 1) { - attributes(mat)$middle <- as.POSIXct(.jcall(m[[t]], "J", "getDateTimeLong"), origin = "1970-01-01") # add the mid-point date/time around which the time window is centered if the time window algorithm was used - } - attributes(mat)$numStatements <- .jcall(m[[t]], "I", "getNumStatements") # add the number of filtered statements the matrix is based on as an attribute to the matrix - attributes(mat)$call <- match.call() # add the arguments of the call as an attribute to the matrix - class(mat) <- c(paste0("dna_network_", networkType), class(mat)) # add "dna_network_onemode" or "dna_network_twomode" as a class label in addition to "matrix" - l[[t]] <- mat # add the matrix to the list - } - if (length(m) == 1) { - return(l[[1]]) # return the first matrix in the list if no time window was used - } else { - attributes(l)$call <- match.call() # add arguments of the call as an attribute also to the list, not just each network matrix - class(l) <- c(paste0("dna_network_", networkType, "_timewindows"), class(l)) # add "dna_network_onemode_timewindows" or "dna_network_twomode_timewindows" to class label - return(l) # return the list of network matrices - } - } -} - -#' Convert a \code{dna_network_onemode} object to a matrix -#' -#' Convert a \code{dna_network_onemode} object to a matrix. -#' -#' Remove the attributes and \code{"dna_network_onemode"} class label from a -#' \code{dna_network_onemode} object and return it as a numeric matrix. -#' -#' @param x The \code{dna_network_onemode} object, as returned by the -#' \code{\link{dna_network}} function. -#' @param ... Additional arguments. Currently not in use. -#' -#' @author Philip Leifeld -#' -#' @family networks -#' -#' @export -as.matrix.dna_network_onemode <- function(x, ...) { - attr(x, "start") <- NULL - attr(x, "stop") <- NULL - attr(x, "numStatements") <- NULL - attr(x, "call") <- NULL - attr(x, "class") <- NULL - return(x) -} - -#' Convert a \code{dna_network_twomode} object to a matrix -#' -#' Convert a \code{dna_network_twomode} object to a matrix. -#' -#' Remove the attributes and \code{"dna_network_twomode"} class label from a -#' \code{dna_network_twomode} object and return it as a numeric matrix. -#' -#' @param x The \code{dna_network_twomode} object, as returned by the -#' \code{\link{dna_network}} function. -#' @param ... Additional arguments. Currently not in use. -#' -#' @author Philip Leifeld -#' -#' @family networks -#' -#' @export -as.matrix.dna_network_twomode <- as.matrix.dna_network_onemode - -#' Print a \code{dna_network_onemode} object -#' -#' Show details of a \code{dna_network_onemode} object. -#' -#' Print a one-mode network matrix and its attributes. -#' -#' @param x A \code{dna_network_onemode} object, as returned by the -#' \code{\link{dna_network}} function. -#' @param trim Number of maximum characters to display in row and column labels -#' of the matrix. Labels with more characters are truncated, and the last -#' character is replaced by an asterisk (\code{*}). -#' @param attr Display attributes, such as the start and stop date and time, the -#' number of statements on which the matrix is based, the function call and -#' arguments on which the network matrix is based, and the full labels without -#' truncation. -#' @param ... Additional arguments. Currently not in use. -#' -#' @author Philip Leifeld -#' -#' @family networks -#' -#' @export -print.dna_network_onemode <- function(x, trim = 5, attr = TRUE, ...) { - rn <- rownames(x) - cn <- colnames(x) - rownames(x) <- sapply(rownames(x), function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) - colnames(x) <- sapply(colnames(x), function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) - x <- round(x, 2) - if ("dna_network_onemode" %in% class(x)) { - onemode <- TRUE - class(x) <- class(x)[class(x) != "dna_network_onemode"] - } else { - onemode <- FALSE - class(x) <- class(x)[class(x) != "dna_network_twomode"] - } - start <- attr(x, "start") - attr(x, "start") <- NULL - stop <- attr(x, "stop") - attr(x, "stop") <- NULL - ns <- attr(x, "numStatements") - attr(x, "numStatements") <- NULL - cl <- deparse(attr(x, "call")) - attr(x, "call") <- NULL - attr(x, "class") <- NULL - print(x) - if (attr) { - cat("\nStart:", as.character(start)) - cat("\nStop: ", as.character(stop)) - cat("\nStatements:", ns) - cat("\nCall:", trimws(cl)) - if (onemode) { - cat("\n\nLabels:\n") - cat(paste(1:length(rn), rn), sep = "\n") - } else { - cat("\n\nRow labels:\n") - cat(paste(1:length(rn), rn), sep = "\n") - cat("\nColumn labels:\n") - cat(paste(1:length(cn), cn), sep = "\n") - } - } -} - -#' Print a \code{dna_network_twomode} object -#' -#' Show details of a \code{dna_network_twomode} object. -#' -#' Print a two-mode network matrix and its attributes. -#' -#' @inheritParams print.dna_network_onemode -#' -#' @author Philip Leifeld -#' -#' @family networks -#' -#' @export -print.dna_network_twomode <- print.dna_network_onemode - -#' Plot networks created using rDNA. -#' -#' Plot a network generated using \code{\link{dna_network}}. -#' -#' These functions plot \code{dna_network_onemode} and -#' \code{dna_network_onemode} objects generated by the \code{\link{dna_network}} -#' function. In order to use this function, please install the \code{igraph} and -#' \code{ggraph} packages. Different layouts for one- and two-mode networks are -#' available. -#' -#' @param object A \code{dna_network} object. -#' @param ... Additional arguments; currently not in use. -#' @param atts A \code{dna_attributes} object generated by -#' \code{\link{dna_getAttributes}}. Provide this object and matching -#' attributes when plotting custom node colors, node labels and/or node sizes. -#' @param layout The type of node layout to use. The following layouts are -#' available from the \code{igraph} and \code{ggraph} packages at the time of -#' writing: -#' \itemize{ -#' \item \code{"stress"} (the default layout) -#' \item \code{"bipartite"} (only for two-mode networks) -#' \item \code{"backbone"} -#' \item \code{"circle"} -#' \item \code{"dh"} -#' \item \code{"drl"} -#' \item \code{"fr"} -#' \item \code{"gem"} -#' \item \code{"graphopt"} -#' \item \code{"kk"} -#' \item \code{"lgl"} -#' \item \code{"mds"} -#' \item \code{"nicely"} -#' \item \code{"randomly"} -#' \item \code{"star"} -#' } -#' See \link[ggraph]{layout_tbl_graph_igraph} for the current list of layouts. -#' @param edge_size_range Two values indicating the minimum and maximum value -#' to scale edge widths. -#' @param edge_color Provide the name of a color for edge colors. The default -#' \code{"NULL"} colors edges in line with the specified -#' \code{qualifierAggregation} in \code{\link{dna_network}}. -#' @param edge_alpha Takes numeric values to control the alpha-transparency of -#' edges. Possible values range from \code{0} (fully transparent) to \code{1} -#' (fully visible). -#' @param node_size Takes positive numeric values to control the size of nodes. -#' Also accepts numeric values matching an attribute of the \code{atts} object -#' (see examples). -#' @param node_colors Provide the name of a color or use an attribute from the -#' \code{atts} object for node colors (see examples). Defaults to -#' \code{"black"}. -#' @param node_label If \code{TRUE}, the row names (in a one-mode network) or -#' the row and column names (in a two-mode network) of the network matrix are -#' used for node labels. Also accepts character objects matching one of the -#' attribute variables of the \code{atts} object (see examples). \code{FALSE} -#' turns off node labels. -#' @param font_size Controls the font size of the node labels. -#' @param truncate Sets the number of characters to which node labels should be -#' truncated. -#' @param threshold Minimum threshold for which edges should be plotted. -#' @param giant_component Only plot the giant component (the biggest connected -#' cluster) of the network. Defaults to \code{FALSE}. -#' @param exclude_isolates Exclude isolates (nodes with no connection to other -#' nodes) from the plot. Defaults to \code{FALSE}. -#' @param max_overlaps Value to exclude node labels that overlap with too many -#' other node labels (see \code{\link[ggrepel]{geom_label_repel}}. Defaults -#' to \code{10}. -#' @param seed Numeric value passed to \link{set.seed}. Ensures that plots are -#' reproducible. -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' ## one-mode network examples -#' -#' # compute network matrix (subtract + normalization) -#' nw <- dna_network(networkType = "onemode", -#' qualifierAggregation = "subtract", -#' normalization = "average") -#' -#' # plot network -#' library("ggplot2") -#' autoplot(nw) -#' -#' # plot only positively weighted edges -#' autoplot(nw, threshold = 0) -#' -#' # congruence network -#' nw <- dna_network(networkType = "onemode", -#' qualifierAggregation = "congruence", -#' excludeValues = list("concept" = -#' c("There should be legislation to regulate emissions."))) -#' autoplot(nw) -#' -#' # use entity colors (here: colors of organizations) from attributes -#' atts <- dna_getAttributes(variableId = 2) -#' autoplot(nw, atts = atts, node_colors = "color", layout = "fr") -#' -#' # use colors from attributes (after editing some of them) -#' atts$color[atts$Type == "NGO"] <- "red" # change NGO color to red -#' atts$color[atts$Type == "Government"] <- "blue" # change government to blue -#' autoplot(nw, atts = atts, node_colors = "color") # plot with custom colors -#' -#' # use an attribute, such as type, to plot node labels -#' autoplot(nw, atts = atts, node_label = "Type") -#' -#' # plot node sizes according to the number of statements of entities; -#' # first, compute additional matrix to calculate the number of statements -#' nw_freq <- dna_network(networkType = "twomode", -#' qualifierAggregation = "ignore", -#' normalization = "no") -#' # then add frequency of statements as an attribute -#' atts$freq <- rowSums(nw_freq)[match(atts$value, rownames(nw_freq))] -#' # plot network with node sizes matching statement frequencies -#' autoplot(nw, atts = atts, node_size = "freq", node_colors = "color") -#' -#' # use igraph community detection for identification of network clusters; -#' # remove negative edge weights -#' nw[nw < 0] <- 0 -#' # convert dna_network to igraph object -#' graph <- igraph::graph_from_adjacency_matrix(nw, -#' mode = "undirected", -#' weighted = TRUE, -#' diag = FALSE, -#' add.colnames = NULL, -#' add.rownames = NA) -#' # compute communities using igraph cluster algorithms -#' # (here: fast and greedy as an illustration)) -#' com <- igraph::cluster_fast_greedy(graph) -#' # add node community membership as an attribute -#' atts$membership <- com$membership[match(atts$value, com$names)] -#' # use community membership as node color -#' autoplot(nw, atts = atts, node_colors = "membership") -#' # or plot ellipses using ggforce package -#' library("ggforce") -#' autoplot(nw, atts = atts, node_colors = "color") + -#' geom_mark_ellipse(aes(x = x, -#' y = y, -#' group = com$membership, -#' fill = com$membership), -#' show.legend = FALSE) -#' -#' # add legend to the network plot (here: colors mapped to type attribute) -#' autoplot(nw, atts = atts, node_colors = "color") + -#' scale_color_identity(name = "", -#' labels = c("Government", "NGO", "Business"), -#' guide = "legend") + -#' theme(legend.position = "bottom", # change legend position -#' legend.text = element_text(size = 10)) # change legend font size -#' -#' ## two-mode network examples -#' -#' # compute two-mode network and plot it -#' nw <- dna_network(networkType = "twomode", -#' qualifierAggregation = "combine") -#' library("ggplot2") -#' autoplot(nw) -#' -#' # use entity colours (here: colors of organizations); -#' # first, retrieve attributes for first-mode entities (organizations) -#' atts <- dna_getAttributes(variableId = 2) -#' # then, retrieve attributes for second-mode entities (concepts) -#' atts2 <- dna_getAttributes(variableId = 3) -#' # combine both attribute objects -#' atts <- rbind(atts, atts2) -#' # plot the network using the attributes of both variables -#' autoplot(nw, -#' atts = atts, -#' node_colors = "color", -#' layout = "bipartite", -#' max_overlaps = 20) -#' # edit the colors before plotting -#' atts$color[atts$Type == "NGO"] <- "red" # change NGO color to red -#' atts$color[atts$Type == "Government"] <- "blue" # government actors in blue -#' # plot the network with custom colors -#' autoplot(nw, atts = atts, node_colors = "color") -#' -#' # use an attribute, such as type, to plot node labels -#' nw <- dna_network(networkType = "twomode", -#' qualifierAggregation = "subtract", -#' normalization = "activity") -#' autoplot(nw, atts = atts, node_label = "Type") -#' -#' # plot node sizes according the number of statements of entities; -#' # first, compute network matrix for plotting -#' nw <- dna_network(networkType = "twomode", -#' qualifierAggregation = "subtract", -#' normalization = "activity") -#' # compute dna_attributes objects -#' atts <- dna_getAttributes(variableId = 2) -#' atts2 <- dna_getAttributes(variableId = 3) -#' # compute additional matrix to calculate the number of statements -#' nw_freq <- dna_network(networkType = "twomode", -#' qualifierAggregation = "ignore", -#' normalization = "no") -#' # add frequency of statements as attribute -#' # compute statement frequencies of first-mode entities -#' atts$freq <- rowSums(nw_freq)[match(atts$value, rownames(nw_freq))] -#' # compute statement frequencies of second-mode entities -#' atts2$freq <- colSums(nw_freq)[match(atts2$value, colnames(nw_freq))] -#' # combine both attribute objects -#' atts <- rbind(atts, atts2) -#' # plot network with node sizes matching statement frequencies -#' autoplot(nw, atts = atts, node_size = "freq", node_colors = "color") -#' -#' # use igraph community detection for identification of network clusters -#' nw <- dna_network(networkType = "twomode", -#' qualifierAggregation = "subtract", -#' normalization = "activity") -#' # compute dna_attributes objects and combine them -#' atts <- dna_getAttributes(variableId = 2) -#' atts2 <- dna_getAttributes(variableId = 3) -#' atts <- rbind(atts, atts2) -#' # remove negative edge weights -#' nw[nw < 0] <- 0 -#' # convert dna_network to igraph object -#' graph <- igraph::graph_from_incidence_matrix(nw, -#' directed = FALSE, -#' weighted = TRUE, -#' add.names = NULL) -#' # compute communities using igraph cluster algorithms -#' # (here: fast and greedy as an illustration)) -#' com <- igraph::cluster_fast_greedy(graph) -#' # add node community membership as an attribute -#' atts$membership <- com$membership[match(atts$value, com$names)] -#' # use community membership as node color -#' autoplot(nw, atts = atts, node_colors = "membership") -#' # or plot ellipses using ggforce -#' library("ggforce") -#' autoplot(nw, atts = atts, node_colors = "color") + -#' geom_mark_ellipse(aes(x = x, -#' y = y, -#' group = com$membership, -#' fill = com$membership), -#' show.legend = FALSE) -#' } -#' -#' @author Tim Henrichsen -#' -#' @family networks -#' -#' @importFrom ggplot2 autoplot -#' @importFrom ggplot2 aes -#' @importFrom ggplot2 scale_color_identity -#' @importFrom rlang .data -#' @name autoplot.dna_network -NULL - -#' @rdname autoplot.dna_network -#' @export -autoplot.dna_network_onemode <- function(object, - ..., - atts = NULL, - layout = "auto", - edge_size_range = c(0.2, 2), - edge_color = NULL, - edge_alpha = 1, - node_size = 3, - node_colors = "black", - node_label = TRUE, - font_size = 6, - truncate = 50, - threshold = NULL, - giant_component = FALSE, - exclude_isolates = FALSE, - max_overlaps = 10, - seed = 12345) { - set.seed(seed) - - if (!grepl("dna_network", class(object)[1])) { - stop("Invalid data object. Please compute a dna_network object with the ", - "dna_network() function before plotting.") - } - - if (!requireNamespace("igraph", quietly = TRUE)) { - stop("The autoplot function requires the 'igraph' package to be installed.\n", - "To do this, enter 'install.packages(\"igraph\")'.") - } - - if (!requireNamespace("ggraph", quietly = TRUE)) { - stop("The autoplot function requires the 'ggraph' package to be installed.\n", - "To do this, enter 'install.packages(\"ggraph\")'.") - } - - if (!is.null(atts) & !"dna_attributes" %in% class(atts)) { - stop("Object provided in 'atts' is not a dna_attributes object. Please ", - "provide a dna_attributes object using dna_getAttributes() or set atts ", - "to NULL if you do not want to use DNA attributes.") - } - - if (!is.numeric(truncate)) { - truncate <- Inf - warning("No numeric value provided for trimming of entities. Truncation ", - "will be ignored.") - } - - # Convert network matrix to igraph network - if ("dna_network_onemode" %in% class(object)) { - graph <- igraph::graph_from_adjacency_matrix(object, - mode = "undirected", - weighted = TRUE, - diag = FALSE, - add.colnames = NULL, - add.rownames = NA) - igraph::V(graph)$shape <- "circle" - } else if ("dna_network_twomode" %in% class(object)) { - graph <- igraph::graph_from_incidence_matrix(object, - directed = FALSE, - weighted = TRUE, - add.names = NULL) - igraph::V(graph)$shape <- ifelse(igraph::V(graph)$type, "square", "circle") - } - - # Check if all entities are included in attributes object (if provided) - if (!is.null(atts) & !(all(igraph::V(graph)$name %in% atts$value))) { - miss <- which(!igraph::V(graph)$name %in% atts$value) - stop("Some network entities are missing in the attributes object:\n", - paste(igraph::V(graph)$name[miss], collapse = "\n")) - } - - # Remove tie weights below threshold - if (!is.null(threshold)) { - graph <- igraph::delete_edges(graph, which(!igraph::E(graph)$weight >= threshold)) - } - - # Add node colors - if (is.character(node_colors)) { - if (!is.null(atts) & length(node_colors) == 1 && node_colors %in% colnames(atts)) { - col_pos <- which(colnames(atts) == node_colors) - igraph::V(graph)$color <- atts[match(igraph::V(graph)$name, atts$value), col_pos] - } else if (length(node_colors) > 1 & length(node_colors) != igraph::vcount(graph)) { - stop("Number of custom colors does not equal number of nodes in the network.") - } else { - igraph::V(graph)$color <- node_colors - } - } else { - igraph::V(graph)$color <- "black" - } - - # Add edge colors - if (is.null(edge_color)) { - if ("combine" %in% as.character(attributes(object)$call)) { - igraph::E(graph)$color <- "green" - igraph::E(graph)$color[igraph::E(graph)$weight == 2] <- "red" - igraph::E(graph)$color[igraph::E(graph)$weight == 3] <- "blue" - # Change edge weight for networks with combine aggregation - igraph::E(graph)$weight[igraph::E(graph)$weight > 0] <- 1 - } else if ("subtract" %in% as.character(attributes(object)$call)) { - igraph::E(graph)$color <- "green" - igraph::E(graph)$color[igraph::E(graph)$weight < 0] <- "red" - } else if ("congruence" %in% as.character(attributes(object)$call)) { - igraph::E(graph)$color <- "green" - } else if ("conflict" %in% as.character(attributes(object)$call)) { - igraph::E(graph)$color <- "red" - } else { - igraph::E(graph)$color <- "gray" - } - } else if (!all(is.na(edge_color))) { - if (length(edge_color) > 1 & length(edge_color) != igraph::ecount(graph)) { - igraph::E(graph)$color <- "gray" - warning("Number of custom edge_colors does not match number of edges ", - "in the network. Will set edge_color to default (gray).") - } else { - igraph::E(graph)$color <- edge_color - } - } else { - igraph::E(graph)$color <- "gray" - } - - # Add node size(s) - if (length(node_size) > 1 & length(node_size) != igraph::vcount(graph)) { - igraph::V(graph)$size <- 7 - warning("Number of provided node size values does not equal number of ", - "nodes in the network. node_size will be set to default value (7).") - } else if (is.character(node_size) & length(node_size) == 1 & !is.null(atts) && node_size %in% colnames(atts)) { - col_pos <- which(colnames(atts) == node_size) - igraph::V(graph)$size <- atts[match(igraph::V(graph)$name, atts$value), col_pos] - } else if (is.numeric(node_size)) { - igraph::V(graph)$size <- node_size - } - - # Add labels - if (!is.logical(node_label)) { - if (is.character(node_label) & length(node_label) == 1 & !is.null(atts) && node_label %in% colnames(atts)) { - col_pos <- which(colnames(atts) == node_label) - igraph::V(graph)$name <- atts[match(igraph::V(graph)$name, atts$value), col_pos] - } else if (!is.null(node_label)) { - if (length(node_label) > 1 & length(node_label) != igraph::vcount(graph)) { - stop("Number of custom labels does not equal number of nodes in the network.") - } - igraph::V(graph)$name <- node_label - } - } - - # Remove isolates - if (exclude_isolates) { - graph <- igraph::delete_vertices(graph, igraph::degree(graph) == 0) - } - - # Only plot giant component of network. Useful for some plotting algorithms. - if (giant_component) { - # Get giant component - components <- igraph::clusters(graph) - biggest_cluster_id <- which.max(components$csize) - - # Get members of giant component - vert_ids <- igraph::V(graph)[components$membership == biggest_cluster_id] - - # Create subgraph - graph <- igraph::induced_subgraph(graph, vert_ids) - } - - - # Truncate labels of entities - igraph::V(graph)$name <- sapply(igraph::V(graph)$name, function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e) - - # Use absolute edge weight values for plotting - igraph::E(graph)$weight <- abs(igraph::E(graph)$weight) - - # Start network plot - g <- ggraph::ggraph(graph, layout = layout, ...) + - suppressWarnings(ggraph::geom_edge_link(ggplot2::aes(edge_width = igraph::E(graph)$weight, edge_colour = igraph::E(graph)$color), - alpha = edge_alpha, - show.legend = FALSE)) + # add edges - ggraph::scale_edge_width(range = edge_size_range) + # add edge scale - ggraph::geom_node_point(ggplot2::aes(colour = igraph::V(graph)$color), # add nodes - size = igraph::V(graph)$size, - shape = igraph::V(graph)$shape, - show.legend = NA) - # Add labels - if ((!is.null(node_label) && !all(is.na(node_label))) && (is.character(node_label) || node_label == TRUE)) { - g <- g + - ggraph::geom_node_text(ggplot2::aes(label = igraph::V(graph)$name), - repel = TRUE, - max.overlaps = max_overlaps, - show.legend = FALSE) - } - - # Add theme and set node colors and edges to identity - g <- g + - ggraph::theme_graph(base_family = "", base_size = font_size) + - ggplot2::scale_color_identity() + - ggraph::scale_edge_color_identity() - - return(g) -} - -#' @rdname autoplot.dna_network -#' @export -autoplot.dna_network_twomode <- autoplot.dna_network_onemode - -#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object -#' -#' Convert a DNA network into a \code{tbl_graph} or \code{graph} object. -#' -#' Convert a \code{dna_network_onemode} or \code{dna_network_twomode} object -#' into a \code{tbl_graph} object as defined in the tidygraph package. These -#' objects can then be plotted using the ggraph package, which contains many -#' network layouts. -#' -#' \code{tbl_graph} objects are an extension of \code{graph} -#' objects defined in the igraph package. Functions for manipulating or plotting -#' the resulting objects from either the tidygraph or igraph package or both -#' can be used. -#' -#' The resulting objects can also be converted to \code{network} objects as -#' defined in the network package (part of the statnet suite of packages) using -#' the \code{asNetwork} function in the intergraph package. -#' -#' @param network A \code{dna_network_onemode} or \code{dna_network_twomode} -#' object to be converted into a \code{tbl_graph} object. Can also be a matrix -#' with edge weights and row and column names for the node labels. -#' @param attributes A \code{dna_attributes} object created using the -#' \link{dna_getAttributes} function with attributes for the nodes in the -#' network. Can also be a data frame with a \code{values} column that contains -#' the node labels and further columns containing the attributes. The -#' attributes are saved as node attributes in the \code{tbl_graph} object. If -#' \code{NULL}, no attributes are included. -#' @param ... Further arguments. Currently not in use. -#' -#' @examples -#' \dontrun{ -#' # prepare toy data -#' dna_sample() -#' dna_openDatabase("sample.dna", coderPassword = "sample") -#' nw <- dna_network(networkType = "onemode", -#' qualifierAggregation = "congruence", -#' excludeValues = list(concept = -#' "There should be legislation to regulate emissions.")) -#' at <- dna_getAttributes(variableId = 2) -#' -#' # convert to tbl_graph object -#' g <- dna_tidygraph(nw, at) -#' -#' # basic visualization -#' ggraph::ggraph(g, layout = "fr") + -#' ggraph::geom_edge_link() + -#' ggraph::geom_node_point() -#' -#' # visualization with more bells and whistles -#' ggraph::ggraph(g, layout = "graphopt") + -#' ggraph::geom_edge_link(ggplot2::aes(color = weight, width = weight)) + -#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + -#' ggplot2::scale_color_identity() + -#' ggraph::scale_edge_color_gradient(low = "azure2", high = "azure4") + -#' ggraph::theme_graph(background = "white") + -#' ggraph::geom_node_text(ggplot2::aes(label = name), -#' repel = TRUE, -#' max.overlaps = 10, -#' show.legend = FALSE) -#' # for more layouts, see vignette("Layouts", package = "ggraph") -#' -#' # hive plot example -#' g <- g |> -#' tidygraph::activate(nodes) |> -#' tidygraph::mutate(centrality = tidygraph::centrality_betweenness()) -#' ggraph::ggraph(g, layout = "hive", axis = Type, sort.by = centrality) + -#' ggraph::geom_edge_hive(ggplot2::aes(colour = "gray", width = weight)) + -#' ggraph::geom_axis_hive(ggplot2::aes(colour = color), -#' size = 5, -#' label = TRUE) + -#' ggraph::scale_edge_color_identity() + -#' theme(legend.position = "none") -#' -#' # example with negative edge weights -#' nw <- dna_network(networkType = "onemode", -#' qualifierAggregation = "subtract", -#' excludeValues = list(concept = -#' "There should be legislation to regulate emissions.")) -#' g <- dna_tidygraph(nw, at) -#' ggraph::ggraph(g, layout = "linear", circular = TRUE) + -#' ggraph::geom_edge_arc(aes(color = color, width = abs)) + -#' ggraph::scale_edge_color_identity() + -#' ggraph::geom_node_point(ggplot2::aes(color = color), size = 5) + -#' ggplot2::scale_color_identity() + -#' ggraph::theme_graph(background = "white") + -#' theme(legend.position = "none") + -#' ggraph::geom_node_text(ggplot2::aes(label = name), -#' repel = TRUE, -#' max.overlaps = 10, -#' show.legend = FALSE) -#' -#' # example with a two-mode network -#' nw <- dna_network(networkType = "twomode", -#' qualifierAggregation = "combine") -#' at1 <- dna_getAttributes(statementTypeId = 1, variable = "organization") -#' at2 <- dna_getAttributes(statementTypeId = 1, variable = "concept") -#' at1$Notes <- "organization" -#' at2$Notes <- "concept" -#' at <- rbind(at1, at2) -#' g <- dna_tidygraph(nw, at) -#' ggraph::ggraph(g, layout = "graphopt") + -#' ggraph::geom_edge_link(ggplot2::aes(color = color), width = 1) + -#' ggraph::scale_edge_color_identity() + -#' ggraph::geom_node_point(ggplot2::aes(color = color, shape = Notes), -#' size = 5) + -#' ggplot2::scale_color_identity() + -#' ggraph::geom_node_text(ggplot2::aes(label = name), -#' repel = TRUE, -#' max.overlaps = 10, -#' show.legend = FALSE) + -#' ggraph::theme_graph(background = "white") + -#' theme(legend.position = "none") -#' -#' # manipulate and plot using the igraph package -#' library("igraph") -#' class(g) # resulting objects are both tbl_graph and igraph objects -#' igraph::V(g) # get the nodes using igraph functions -#' igraph::E(g) # get the edges using igraph functions -#' igraph::plot(g) # plot network using igraph package -#' -#' # convert to network object (network package, statnet suite of packages) -#' library("intergraph") -#' intergraph::asNetwork(g) -#' } -#' -#' @author Philip Leifeld -#' @family networks -#' @importFrom rlang .data -#' @export -dna_tidygraph <- function(network, attributes = NULL, ...) { - if (length(intersect(c("dna_network_onemode", "dna_network_twomode", "matrix"), class(network))) < 1) { - stop("The 'network' argument must provide an object created by the 'dna_network' function or a matrix.") - } - if (!is.null(attributes) && (length(intersect(c("dna_attributes", "data.frame"), class(attributes))) < 1) || !"value" %in% colnames(attributes)) { - stop("The 'attributes' argument must be NULL or created by the 'dna_getAttributes' function or a data frame with a 'values' column.") - } - if (!requireNamespace("tidygraph", quietly = TRUE) || packageVersion("tidygraph") < "1.3.1") { - stop("The 'dna_tidygraph' function requires the 'tidygraph' package (>= 1.3.1) to be installed.\n", - "To do this, enter 'install.packages(\"tidygraph\")'.") - } - - if ("dna_network_twomode" %in% class(network)) { - nodes <- data.frame(name = c(rownames(network), colnames(network)), type = c(rep(TRUE, nrow(network)), rep(FALSE, ncol(network))), stringsAsFactors = FALSE) - edges <- data.frame(from = rep(rownames(network), times = ncol(network)), to = rep(colnames(network), each = nrow(network)), weight = as.vector(network)) - edges <- edges[edges$weight != 0, ] - edges$from <- match(edges$from, nodes$name) - edges$to <- match(edges$to, nodes$name) - g <- tidygraph::tbl_graph(nodes = nodes, edges = edges, directed = FALSE) # create tbl_graph object for ggraph - } else if ("dna_network_onemode" %in% class(network)) { - g <- tidygraph::as_tbl_graph(network, directed = FALSE) # create tbl_graph object for ggraph - } else { - stop("Argument supplied by argument 'network' not recognized.") - } - - if (!is.null(attributes)) { - nodes <- tidygraph::as_tibble(g, active = "nodes")$name # extract nodes from graph for matching - at <- attributes[attributes$value %in% nodes, ] # retain only those attributes present in the network - at <- at[match(nodes, at$value), ] # sort attributes in the same order as the nodes in the graph - g <- tidygraph::mutate(g, at[, colnames(at) != "value"]) # embed node attributes in graph - } - - edges <- tidygraph::as_tibble(g, active = "edges") # extract edges from graph - u <- unique(edges$weight) # unique edge weights - combined <- length(u) < 5 && any(grepl("combine", attributes(network)$call)) # combined qualifier aggregation? - edgecol <- sapply(edges$weight, function(weight) { # create edge colors - if (length(u) == 2 & all(sort(u) %in% 0:1) & weight > 0) { # binary: 1 = gray - "gray" - } else if (combined) { # "combined" qualifier aggregation - if (weight == 1) { - "green" - } else if (weight == 2) { - "red" - } else if (weight == 3) { - "blue" - } else { - "gray" - } - } else if (any(u < 0)) { # "subtract" (or something else that generates negative ties) - if (weight < 0) { - "red" - } else { - "green" - } - } else { # any other scale, for example "congruence" qualifier aggregation - "gray" - } - }) - g <- g |> # assign absolute values, edge colors, and sign as edge attributes - tidygraph::activate(edges) |> - tidygraph::mutate(abs = abs(.data$weight), - color = .data$edgecol, - sign = ifelse(.data$weight < 0, "negative", "positive")) - - return(g) -} - - -# Barplots --------------------------------------------------------------------- - -#' Generate the data necessary for creating a barplot for a variable -#' -#' Generate the data necessary for creating a barplot for a variable. -#' -#' Create a \code{dna_barplot} object, which contains a data frame with -#' entity value frequencies grouped by the levels of a qualifier variable. -#' The qualifier variable is optional. -#' -#' @param variable The variable for which the barplot will be generated. There -#' will be one bar per entity label of this variable. -#' @param qualifier A boolean (binary) or integer variable to group the value -#' frequencies by. Can be \code{NULL} to skip the grouping. -#' @inheritParams dna_network -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' # compute barplot data -#' b <- dna_barplot(statementType = "DNA Statement", -#' variable = "concept", -#' qualifier = "agreement") -#' b -#' } -#' -#' @author Philip Leifeld -#' -#' @rdname dna_barplot -#' @importFrom rJava .jarray -#' @importFrom rJava .jcall -#' @importFrom rJava .jevalArray -#' @importFrom rJava .jnull -#' @importFrom rJava is.jnull -#' @export -dna_barplot <- function(statementType = "DNA Statement", - variable = "concept", - qualifier = "agreement", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE) { - - # wrap the vectors of exclude values for document variables into Java arrays - excludeAuthors <- .jarray(excludeAuthors) - excludeSources <- .jarray(excludeSources) - excludeSections <- .jarray(excludeSections) - excludeTypes <- .jarray(excludeTypes) - - # compile exclude variables and values vectors - dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) - count <- 0 - if (length(excludeValues) > 0) { - for (i in 1:length(excludeValues)) { - if (length(excludeValues[[i]]) > 0) { - for (j in 1:length(excludeValues[[i]])) { - count <- count + 1 - dat[count, 1] <- names(excludeValues)[i] - dat[count, 2] <- excludeValues[[i]][j] - } - } - } - var <- dat[, 1] - val <- dat[, 2] - } else { - var <- character() - val <- character() - } - var <- .jarray(var) # array of variable names of each excluded value - val <- .jarray(val) # array of values to be excluded - - # encode R NULL as Java null value if necessary - if (is.null(qualifier) || is.na(qualifier)) { - qualifier <- .jnull(class = "java/lang/String") - } - - # call rBarplotData function to compute results - b <- .jcall(dnaEnvironment[["dna"]]$headlessDna, - "Lexport/BarplotResult;", - "rBarplotData", - statementType, - variable, - qualifier, - duplicates, - start.date, - stop.date, - start.time, - stop.time, - var, - val, - excludeAuthors, - excludeSources, - excludeSections, - excludeTypes, - invertValues, - invertAuthors, - invertSources, - invertSections, - invertTypes, - simplify = TRUE) - - at <- .jcall(b, "[[Ljava/lang/String;", "getAttributes") - at <- t(sapply(at, FUN = .jevalArray)) - - counts <- .jcall(b, "[[I", "getCounts") - counts <- t(sapply(counts, FUN = .jevalArray)) - if (nrow(counts) < nrow(at)) { - counts <- t(counts) - } - - results <- data.frame(.jcall(b, "[S", "getValues"), - counts, - at) - - intValues <- .jcall(b, "[I", "getIntValues") - intColNames <- intValues - if (is.jnull(qualifier)) { - intValues <- integer(0) - intColNames <- "Frequency" - } - - atVar <- .jcall(b, "[S", "getAttributeVariables") - - colnames(results) <- c("Entity", intColNames, atVar) - - attributes(results)$variable <- .jcall(b, "S", "getVariable") - attributes(results)$intValues <- intValues - attributes(results)$attributeVariables <- atVar - - class(results) <- c("dna_barplot", class(results)) - - return(results) -} - -#' Print a \code{dna_barplot} object -#' -#' Show details of a \code{dna_barplot} object. -#' -#' Print the data frame returned by the \code{\link{dna_barplot}} function. -#' -#' @param x A \code{dna_barplot} object, as returned by the -#' \code{\link{dna_barplot}} function. -#' @param trim Number of maximum characters to display in entity labels. -#' Entities with more characters are truncated, and the last character is -#' replaced by an asterisk (\code{*}). -#' @param attr Display attributes, such as the name of the variable and the -#' levels of the qualifier variable if available. -#' @param ... Additional arguments. Currently not in use. -#' -#' @author Philip Leifeld -#' -#' @rdname dna_barplot -#' @export -print.dna_barplot <- function(x, trim = 30, attr = TRUE, ...) { - x2 <- x - if (isTRUE(attr)) { - cat("Variable:", attr(x2, "variable")) - intVal <- attr(x2, "intValues") - if (length(intVal) > 0) { - cat(".\nQualifier levels:", paste(intVal, collapse = ", ")) - } else { - cat(".\nNo qualifier variable") - } - cat(".\n") - } - x2$Entity <- sapply(x2$Entity, function(e) if (nchar(e) > trim) paste0(substr(e, 1, trim - 1), "*") else e) - class(x2) <- "data.frame" - print(x2) -} - -#' Plot \code{dna_barplot} object. -#' -#' Plot a barplot generated from \code{\link{dna_barplot}}. -#' -#' This function plots \code{dna_barplot} objects generated by the -#' \code{\link{dna_barplot}} function. It plots agreement and disagreement with -#' DNA statements for different entities such as \code{"concept"}, -#' \code{"organization"}, or \code{"person"}. Colors can be modified before -#' plotting (see examples). -#' -#' @param object A \code{dna_barplot} object. -#' @param ... Additional arguments; currently not in use. -#' @param lab.pos,lab.neg Names for (dis-)agreement labels. -#' @param lab Should (dis-)agreement labels and title be displayed? -#' @param colors If \code{TRUE}, the \code{Colors} column in the -#' \code{dna_barplot} object will be used to fill the bars. Also accepts -#' character objects matching one of the attribute variables of the -#' \code{dna_barplot} object. -#' @param fontSize Text size in pt. -#' @param barWidth Thickness of the bars. Bars will touch when set to \code{1}. -#' When set to \code{0.5}, space between two bars is the same as thickness of -#' bars. -#' @param axisWidth Thickness of the x-axis which separates agreement from -#' disagreement. -#' @param truncate Sets the number of characters to which axis labels should be -#' truncated. -#' @param exclude.min Reduces the plot to entities with a minimum frequency of -#' statements. -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' # compute barplot data -#' b <- dna_barplot(statementType = "DNA Statement", -#' variable = "concept", -#' qualifier = "agreement") -#' -#' # plot barplot with ggplot2 -#' library("ggplot2") -#' autoplot(b) -#' -#' # use entity colours (here: colors of organizations as an illustration) -#' b <- dna_barplot(statementType = "DNA Statement", -#' variable = "organization", -#' qualifier = "agreement") -#' autoplot(b, colors = TRUE) -#' -#' # edit the colors before plotting -#' b$Color[b$Type == "NGO"] <- "red" # change NGO color to red -#' b$Color[b$Type == "Government"] <- "blue" # change government color to blue -#' autoplot(b, colors = TRUE) -#' -#' # use an attribute, such as type, to color the bars -#' autoplot(b, colors = "Type") + -#' scale_colour_manual(values = "black") -#' -#' # replace colors for the three possible actor types with custom colors -#' autoplot(b, colors = "Type") + -#' scale_fill_manual(values = c("red", "blue", "green")) + -#' scale_colour_manual(values = "black") -#' } -#' -#' @author Johannes B. Gruber, Tim Henrichsen -#' -#' @rdname dna_barplot -#' @importFrom ggplot2 autoplot -#' @importFrom ggplot2 ggplot -#' @importFrom ggplot2 aes -#' @importFrom ggplot2 geom_line -#' @importFrom ggplot2 theme_minimal -#' @importFrom ggplot2 theme -#' @importFrom ggplot2 geom_bar -#' @importFrom ggplot2 position_stack -#' @importFrom ggplot2 coord_flip -#' @importFrom ggplot2 element_blank -#' @importFrom ggplot2 element_text -#' @importFrom ggplot2 scale_color_identity -#' @importFrom ggplot2 scale_fill_identity -#' @importFrom ggplot2 geom_text -#' @importFrom ggplot2 .pt -#' @importFrom ggplot2 annotate -#' @importFrom ggplot2 scale_x_discrete -#' @importFrom utils stack -#' @importFrom grDevices col2rgb -#' @importFrom rlang .data -#' @export -autoplot.dna_barplot <- function(object, - ..., - lab.pos = "Agreement", - lab.neg = "Disagreement", - lab = TRUE, - colors = FALSE, - fontSize = 12, - barWidth = 0.6, - axisWidth = 1.5, - truncate = 40, - exclude.min = NULL) { - - - if (!("dna_barplot" %in% class(object))) { - stop("Invalid data object. Please compute a dna_barplot object via the ", - "dna_barplot function before plotting.") - } - - if (!("Entity" %in% colnames(object))) { - stop("dna_barplot object does not have a \'Entity\' variable. Please ", - "compute a new dna_barplot object via the dna_barplot function before", - " plotting.") - } - - if (isTRUE(colors) & !("Color" %in% colnames(object)) | - is.character(colors) & !(colors %in% colnames(object))) { - colors <- FALSE - warning("No color variable found in dna_barplot object. Colors will be", - " ignored.") - } - - if (!is.numeric(truncate)) { - truncate <- Inf - warning("No numeric value provided for trimming of entities. Truncation ", - "will be ignored.") - } - - # Get qualifier values - w <- attr(object, "intValues") - - if (!all(w %in% colnames(object))) { - stop("dna_barplot object does not include all qualifier values of the ", - "statement type. Please compute a new dna_barplot object via the ", - "dna_barplot function.") - } - - # Check if qualifier is binary - binary <- all(w %in% c(0, 1)) - - # Compute total values per entity - object$sum <- rowSums(object[, colnames(object) %in% w]) - - # Exclude minimum number of statements per entity - if (is.numeric(exclude.min)) { - if (exclude.min > max(object$sum)) { - exclude.min <- NULL - warning("Value provided in exclude.min is higher than maximum frequency ", - "of entity (", max(object$sum), "). Will ignore exclude.min.") - } else { - object <- object[object$sum >= exclude.min, ] - } - } - - # Stack agreement and disagreement - object2 <- cbind(object$Entity, utils::stack(object, select = colnames(object) %in% w)) - colnames(object2) <- c("entity", "frequency", "agreement") - - object <- object[order(object$sum, decreasing = TRUE), ] - - object2$entity <- factor(object2$entity, levels = rev(object$Entity)) - - # Get colors - if (isTRUE(colors)) { - object2$color <- object$Color[match(object2$entity, object$Entity)] - object2$text_color <- "black" - # Change text color to white in case of dark bar colors - object2$text_color[sum(grDevices::col2rgb(object2$color) * c(299, 587, 114)) / 1000 < 123] <- "white" - } else if (is.character(colors)) { - object2$color <- object[, colors][match(object2$entity, object$Entity)] - object2$text_color <- "black" - } else { - object2$color <- "white" - object2$text_color <- "black" - } - - - if (binary) { - # setting disagreement as -1 instead 0 - object2$agreement <- ifelse(object2$agreement == 0, -1, 1) - # recode frequency in positive and negative - object2$frequency <- object2$frequency * as.integer(object2$agreement) - - # generate position of bar labels - offset <- (max(object2$frequency) + abs(min(object2$frequency))) * 0.05 - offset <- ifelse(offset < 0.5, 0.5, offset) # offset should be at least 0.5 - if (offset > abs(min(object2$frequency))) { - offset <- abs(min(object2$frequency)) - } - if (offset > max(object2$frequency)) { - offset <- abs(min(object2$frequency)) - } - object2$pos <- ifelse(object2$frequency > 0, - object2$frequency + offset, - object2$frequency - offset) - - # move 0 labels where necessary - object2$pos[object2$frequency == 0] <- ifelse(object2$agreement[object2$frequency == 0] == 1, - object2$pos[object2$frequency == 0] * -1, - object2$pos[object2$frequency == 0]) - object2$label <- as.factor(abs(object2$frequency)) - } else { - object2$count <- object2$frequency - # set frequency of negative qualifiers to negative values - object2$frequency <- ifelse(as.numeric(as.character(object2$agreement)) >= 0, object2$frequency, - object2$frequency * -1) - # remove zero frequencies - object2 <- object2[object2$frequency != 0, ] - # generate position of bar labels - object2$pos <- ifelse(object2$frequency > 0, - 1.1, - -0.1) - # Add labels - object2$label <- paste(object2$count, object2$agreement, sep = " x ") - } - - offset <- (max(object2$frequency) + abs(min(object2$frequency))) * 0.05 - offset <- ifelse(offset < 0.5, 0.5, offset) - yintercepts <- data.frame(x = c(0.5, length(unique(object2$entity)) + 0.5), - y = c(0, 0)) - high <- yintercepts$x[2] + 0.25 - - object2 <- object2[order(as.numeric(as.character(object2$agreement)), - decreasing = FALSE), ] - object2$agreement <- factor(object2$agreement, levels = w) - - # Plot - g <- ggplot2::ggplot(object2, - ggplot2::aes(x = .data[["entity"]], - y = .data[["frequency"]], - fill = .data[["agreement"]], - group = .data[["agreement"]], - label = .data[["label"]])) - if (binary) { # Bars for the binary case - g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], - color = .data[["text_color"]]), - stat = "identity", - width = barWidth, - show.legend = FALSE) - # For the integer case with positive and negative values - } else if (max(w) > 0 & min(w) < 0) { - g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], - color = .data[["text_color"]]), - stat = "identity", - width = barWidth, - show.legend = FALSE, - data = object2[as.numeric(as.character(object2$agreement)) >= 0, ], - position = ggplot2::position_stack(reverse = TRUE)) + - ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], - color = .data[["text_color"]]), - stat = "identity", - width = barWidth, - show.legend = FALSE, - data = object2[as.numeric(as.character(object2$agreement)) < 0, ]) - # For the integer case with positive values only - } else if (min(w) >= 0) { - g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], - color = .data[["text_color"]]), - stat = "identity", - width = barWidth, - show.legend = FALSE, - position = ggplot2::position_stack(reverse = TRUE)) - # For the integer case with negative values only - } else { - g <- g + ggplot2::geom_bar(ggplot2::aes(fill = .data[["color"]], - color = .data[["text_color"]]), - stat = "identity", - width = barWidth, - show.legend = FALSE) - } - g <- g + ggplot2::coord_flip() + - ggplot2::theme_minimal() + - # Add intercept line - ggplot2::geom_line(ggplot2::aes(x = .data[["x"]], y = .data[["y"]]), - data = yintercepts, - linewidth = axisWidth, - inherit.aes = FALSE) + - # Remove all panel grids, axis titles and axis ticks and text for x-axis - ggplot2::theme(panel.grid.major = ggplot2::element_blank(), - panel.grid.minor = ggplot2::element_blank(), - axis.title = ggplot2::element_blank(), - axis.ticks.y = ggplot2::element_blank(), - axis.text.x = ggplot2::element_blank(), - axis.text.y = ggplot2::element_text(size = fontSize)) #+ - if (is.logical(colors)) { - g <- g + ggplot2::scale_fill_identity() + - ggplot2::scale_color_identity() - } - if (binary) { # Add entity labels for binary case - g <- g + - ggplot2::geom_text(ggplot2::aes(x = .data[["entity"]], - y = .data[["pos"]], - label = .data[["label"]]), - size = (fontSize / ggplot2::.pt), - inherit.aes = FALSE, - data = object2) - # Add entity labels for integer case with positive and negative values - } else if (max(w) > 0 & min(w) < 0) { - g <- g + - ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), - size = (fontSize / ggplot2::.pt), - position = ggplot2::position_stack(vjust = 0.5, reverse = TRUE), - inherit.aes = TRUE, - data = object2[object2$frequency >= 0, ]) + - ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), - size = (fontSize / ggplot2::.pt), - position = ggplot2::position_stack(vjust = 0.5), - inherit.aes = TRUE, - data = object2[object2$frequency < 0, ]) - # Add entity labels for integer case with positive values only - } else if (min(w) >= 0) { - g <- g + - ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), - size = (fontSize / ggplot2::.pt), - position = ggplot2::position_stack(vjust = 0.5, reverse = TRUE), - inherit.aes = TRUE) - } else { - g <- g + - ggplot2::geom_text(ggplot2::aes(color = .data[["text_color"]]), - size = (fontSize / ggplot2::.pt), - position = ggplot2::position_stack(vjust = 0.5), - inherit.aes = TRUE) - } - if (lab) { # Add (dis-)agreement labels - g <- g + - ggplot2::annotate("text", - x = high, - y = offset * 2, - hjust = 0, - label = lab.pos, - size = (fontSize / ggplot2::.pt)) + - ggplot2::annotate("text", - x = high, - y = 0 - offset * 2, - hjust = 1, - label = lab.neg, - size = (fontSize / ggplot2::.pt)) + - # Truncate labels of entities - ggplot2::scale_x_discrete(labels = sapply(as.character(object2$entity), function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e), - expand = c(0, 2, 0, 2), - limits = levels(object2$entity)) - } else { - g <- g + - # Truncate labels of entities - ggplot2::scale_x_discrete(labels = sapply(as.character(object2$entity), function(e) if (nchar(e) > truncate) paste0(substr(e, 1, truncate - 1), "*") else e), - limits = levels(object2$entity)) - } - return(g) -} - - -# Backbones -------------------------------------------------------------------- - -#' Compute and retrieve the backbone and redundant set -#' -#' Compute and retrieve the backbone and redundant set of a discourse network. -#' -#' The dna_backbone function applies a simulated annealing algorithm to the -#' discourse network to partition the set of second-mode entities (e.g., -#' concepts) into a backbone set and a complementary redundant set. Three -#' methods are available: -#' \itemize{ -#' \item A simulated annealing algorithm with a penalty. You can play with -#' different penalties and see how they affect the size of your backbone -#' set. -#' \item A modified simulated annealing algorithm for a fixed number of -#' backbone entities to retain. This is computationally simpler, but you -#' have to know how large the set should be. -#' \item A fast and greedy nested algorithm, which evaluates all possible -#' fixed backbone solutions, i.e., for all sizes, and provides a nested -#' hierarchy of entities on the second mode. This algorithm may stay below -#' the optimum and is only an approximation but provides insights into the -#' hierarchy of concepts and their relative importance. -#' } -#' -#' The \code{autoplot} function requires the ggplot2 package and can plot -#' algorithm diagnostics and the hierarchy of entities as a dendrogram, -#' depending on the method that was chosen. The \code{plot} function can do the -#' same thing, just using base plots, not ggplot2. -#' -#' The \code{dna_evaluateBackboneSolution} function computes the spectral loss -#' for an arbitrary backbone and its complement, the redundant set, specified by -#' the user. For example, the user can evaluate how much structure would be lost -#' if the second mode was composed only of the concepts provided to this -#' function. This can be used to compare how useful different codebook models -#' are. The penalty parameter \code{p} applies a penalty factor to the spectral -#' loss. The default value of \code{0} switches off the penalty as it is usually -#' not needed to evaluate a specific solution. The backbone set can be supplied -#' as a vector of character objects, for example concepts. -#' -#' @param method The backbone algorithm used to compute the results. Several -#' methods are available: -#' \itemize{ -#' \item \code{"nested"}: A relatively fast, deterministic algorithm that -#' produces the full hierarchy of entities. It starts with a complete -#' backbone set resembling the full network. There are as many iterations -#' as entities on the second mode. In each iteration, the entity whose -#' removal would yield the smallest backbone loss is moved from the -#' backbone set into the redundant set, and the (unpenalized) spectral -#' loss is recorded. This creates a solution for all backbone sizes, where -#' each backbone set is fully nested in the next larger backbone set. The -#' solution usually resembles an unconstrained solution where nesting is -#' not required, but in some cases the loss of a non-nested solution may be -#' larger at a given level or number of elements in the backbone set. -#' \item \code{"fixed"}: Simulated annealing with a fixed number of elements -#' in the backbone set (i.e., only lateral changes are possible) and -#' without penalty. This method may yield more optimal solutions than the -#' nested algorithm because it does not require a strict hierarchy. -#' However, it produces an approximation of the global optimum and is -#' slower than the nested method. With this method, you can specify that -#' backbone set should have, for example, exactly 10 concepts. Then fewer -#' iterations are necessary than with the penalty method because the search -#' space is smaller. The backbone set size is defined in the -#' \code{"backboneSize"} argument. -#' \item \code{"penalty"}: Simulated annealing with a variable number of -#' elements in the backbone set. The solution is stabilized by a penalty -#' parameter (see \code{"penalty"} argument). This algorithm takes longest -#' to compute for a single solution, and it is only an approximation, but -#' it considers slightly larger or smaller backbone sets if the solution is -#' better, thus this algorithm adds some flexibility. It requires more -#' iterations than the fixed method for achieving the same quality. -#' } -#' @param backboneSize The number of elements in the backbone set, as a fixed -#' parameter. Only used when \code{method = "fixed"}. -#' @param penalty The penalty parameter for large backbone sets. The larger the -#' value, the more strongly larger backbone sets are punished and the smaller -#' the resulting backbone is. Try out different values to find the right size -#' of the backbone set. Reasonable values could be \code{2.5}, \code{5}, -#' \code{7.5}, or \code{12}, for example. The minimum is \code{0.0}, which -#' imposes no penalty on the size of the backbone set and produces a redundant -#' set with only one element. Start with \code{0.0} if you want to weed out a -#' single concept and subsequently increase the penalty to include more items -#' in the redundant set and shrink the backbone further. Only used when -#' \code{method = "penalty"}. -#' @param iterations The number of iterations of the simulated annealing -#' algorithm. More iterations take more time but may lead to better -#' optimization results. Only used when \code{method = "penalty"} or -#' \code{method = "fixed"}. -#' @param qualifierAggregation The aggregation rule for the \code{qualifier} -#' variable. This must be \code{"ignore"} (for ignoring the qualifier -#' variable), \code{"congruence"} (for recording a network tie only if both -#' nodes have the same qualifier value in the binary case or for recording the -#' similarity between the two nodes on the qualifier variable in the integer -#' case), \code{"conflict"} (for recording a network tie only if both nodes -#' have a different qualifier value in the binary case or for recording the -#' distance between the two nodes on the qualifier variable in the integer -#' case), or \code{"subtract"} (for subtracting the conflict tie value from -#' the congruence tie value in each dyad; note that negative values will be -#' replaced by \code{0} in the backbone calculation). -#' @param normalization Normalization of edge weights. Valid settings are -#' \code{"no"} (for switching off normalization), \code{"average"} (for -#' average activity normalization), \code{"jaccard"} (for Jaccard coefficient -#' normalization), and \code{"cosine"} (for cosine similarity normalization). -#' @param fileFormat An optional file format specification for saving the -#' backbone results to a file instead of returning an object. Valid values -#' are \code{"json"}, \code{"xml"}, and \code{NULL} (for returning the results -#' instead of writing them to a file). -#' @inheritParams dna_network -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' # compute backbone and redundant set using penalised spectral loss -#' b <- dna_backbone(method = "penalty", -#' penalty = 3.5, -#' iterations = 10000, -#' variable1 = "organization", -#' variable2 = "concept", -#' qualifier = "agreement", -#' qualifierAggregation = "subtract", -#' normalization = "average") -#' -#' b # display main results -#' -#' # extract results from the object -#' b$backbone # show the set of backbone concepts -#' b$redundant # show the set of redundant concepts -#' b$unpenalized_backbone_loss # spectral loss between full and backbone network -#' b$unpenalized_redundant_loss # spectral loss of redundant network -#' b$backbone_network # show the backbone network -#' b$redundant_network # show the redundant network -#' b$full_network # show the full network -#' -#' # plot diagnostics with base R -#' plot(b, ma = 500) -#' -#' # arrange plots in a 2 x 2 view -#' par(mfrow = c(2, 2)) -#' plot(b) -#' -#' # plot diagnostics with ggplot2 -#' library("ggplot2") -#' p <- autoplot(b) -#' p -#' -#' # pick a specific diagnostic -#' p[[3]] -#' -#' # use the patchwork package to arrange the diagnostics in a single plot -#' library("patchwork") -#' new_plot <- p[[1]] + p[[2]] + p[[3]] + p[[4]] -#' new_plot & theme_grey() + theme(legend.position = "bottom") -#' -#' # use the gridExtra package to arrange the diagnostics in a single plot -#' library("gridExtra") -#' grid.arrange(p[[1]], p[[2]], p[[3]], p[[4]]) -#' -#' # compute backbone with fixed size (here: 4 concepts) -#' b <- dna_backbone(method = "fixed", -#' backboneSize = 4, -#' iterations = 2000, -#' variable1 = "organization", -#' variable2 = "concept", -#' qualifier = "agreement", -#' qualifierAggregation = "subtract", -#' normalization = "average") -#' b -#' -#' # compute backbone with a nested structure and plot dendrogram -#' b <- dna_backbone(method = "nested", -#' variable1 = "organization", -#' variable2 = "concept", -#' qualifier = "agreement", -#' qualifierAggregation = "subtract", -#' normalization = "average") -#' b -#' plot(b) -#' autoplot(b) -#' } -#' -#' @author Philip Leifeld, Tim Henrichsen -#' -#' @rdname dna_backbone -#' @importFrom rJava .jarray -#' @importFrom rJava .jcall -#' @importFrom rJava .jnull -#' @importFrom rJava J -#' @export -dna_backbone <- function(method = "nested", - backboneSize = 1, - penalty = 3.5, - iterations = 10000, - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "subtract", - normalization = "average", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE, - fileFormat = NULL, - outfile = NULL) { - - # wrap the vectors of exclude values for document variables into Java arrays - excludeAuthors <- .jarray(excludeAuthors) - excludeSources <- .jarray(excludeSources) - excludeSections <- .jarray(excludeSections) - excludeTypes <- .jarray(excludeTypes) - - # compile exclude variables and values vectors - dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) - count <- 0 - if (length(excludeValues) > 0) { - for (i in 1:length(excludeValues)) { - if (length(excludeValues[[i]]) > 0) { - for (j in 1:length(excludeValues[[i]])) { - count <- count + 1 - dat[count, 1] <- names(excludeValues)[i] - dat[count, 2] <- excludeValues[[i]][j] - } - } - } - var <- dat[, 1] - val <- dat[, 2] - } else { - var <- character() - val <- character() - } - var <- .jarray(var) # array of variable names of each excluded value - val <- .jarray(val) # array of values to be excluded - - # encode R NULL as Java null value if necessary - if (is.null(qualifier) || is.na(qualifier)) { - qualifier <- .jnull(class = "java/lang/String") - } - if (is.null(fileFormat)) { - fileFormat <- .jnull(class = "java/lang/String") - } - if (is.null(outfile)) { - outfile <- .jnull(class = "java/lang/String") - } - - # call rBackbone function to compute results - .jcall(dnaEnvironment[["dna"]]$headlessDna, - "V", - "rBackbone", - method, - as.integer(backboneSize), - as.double(penalty), - as.integer(iterations), - statementType, - variable1, - variable1Document, - variable2, - variable2Document, - qualifier, - qualifierDocument, - qualifierAggregation, - normalization, - duplicates, - start.date, - stop.date, - start.time, - stop.time, - var, - val, - excludeAuthors, - excludeSources, - excludeSections, - excludeTypes, - invertValues, - invertAuthors, - invertSources, - invertSections, - invertTypes, - outfile, - fileFormat - ) - - exporter <- .jcall(dnaEnvironment[["dna"]]$headlessDna, "Lexport/Exporter;", "getExporter") # get a reference to the Exporter object, in which results are stored - if (!is.null(outfile) && !is.null(fileFormat) && is.character(outfile) && is.character(fileFormat) && fileFormat %in% c("json", "xml")) { - message("File exported.") - } else if (method[1] %in% c("penalty", "fixed")) { - result <- .jcall(exporter, "Lexport/SimulatedAnnealingBackboneResult;", "getSimulatedAnnealingBackboneResult", simplify = TRUE) - # create a list with various results - l <- list() - l$penalty <- .jcall(result, "D", "getPenalty") - if (method[1] == "fixed") { - l$backbone_size <- as.integer(backboneSize) - } else { - l$backbone_size <- as.integer(NA) - } - l$iterations <- .jcall(result, "I", "getIterations") - l$backbone <- .jcall(result, "[S", "getBackboneEntities") - l$redundant <- .jcall(result, "[S", "getRedundantEntities") - l$unpenalized_backbone_loss <- .jcall(result, "D", "getUnpenalizedBackboneLoss") - l$unpenalized_redundant_loss <- .jcall(result, "D", "getUnpenalizedRedundantLoss") - rn <- .jcall(result, "[S", "getLabels") - - # store the three matrices in the result list - fullmat <- .jcall(result, "[[D", "getFullNetwork", simplify = TRUE) - rownames(fullmat) <- rn - colnames(fullmat) <- rn - l$full_network <- fullmat - backbonemat <- .jcall(result, "[[D", "getBackboneNetwork", simplify = TRUE) - rownames(backbonemat) <- rn - colnames(backbonemat) <- rn - l$backbone_network <- backbonemat - redundantmat <- .jcall(result, "[[D", "getRedundantNetwork", simplify = TRUE) - rownames(redundantmat) <- rn - colnames(redundantmat) <- rn - l$redundant_network <- redundantmat - - # store diagnostics per iteration as a data frame - d <- data.frame(iteration = 1:.jcall(result, "I", "getIterations"), - temperature = .jcall(result, "[D", "getTemperature"), - acceptance_prob = .jcall(result, "[D", "getAcceptanceProbability"), - acceptance = .jcall(result, "[I", "getAcceptance"), - penalized_backbone_loss = .jcall(result, "[D", "getPenalizedBackboneLoss"), - proposed_backbone_size = .jcall(result, "[I", "getProposedBackboneSize"), - current_backbone_size = .jcall(result, "[I", "getCurrentBackboneSize"), - optimal_backbone_size = .jcall(result, "[I", "getOptimalBackboneSize"), - acceptance_ratio_ma = .jcall(result, "[D", "getAcceptanceRatioMovingAverage")) - - l$diagnostics <- d - - # store start date/time, end date/time, number of statements, call, and class label in each network matrix - start <- as.POSIXct(.jcall(result, "J", "getStart"), origin = "1970-01-01") # add the start date/time of the result as an attribute to the matrices - attributes(l$full_network)$start <- start - attributes(l$backbone_network)$start <- start - attributes(l$redundant_network)$start <- start - stop <- as.POSIXct(.jcall(result, "J", "getStop"), origin = "1970-01-01") # add the end date/time of the result as an attribute to the matrices - attributes(l$full_network)$stop <- stop - attributes(l$backbone_network)$stop <- stop - attributes(l$redundant_network)$stop <- stop - attributes(l$full_network)$numStatements <- .jcall(result, "I", "getNumStatements") # add the number of filtered statements the matrix is based on as an attribute to the matrix - attributes(l$full_network)$call <- match.call() - attributes(l$backbone_network)$call <- match.call() - attributes(l$redundant_network)$call <- match.call() - attributes(l)$method <- method[1] - class(l$full_network) <- c("dna_network_onemode", class(l$full_network)) - class(l$backbone_network) <- c("dna_network_onemode", class(l$backbone_network)) - class(l$redundant_network) <- c("dna_network_onemode", class(l$redundant_network)) - class(l) <- c("dna_backbone", class(l)) - return(l) - } else if (method[1] == "nested") { - result <- .jcall(exporter, "Lexport/NestedBackboneResult;", "getNestedBackboneResult", simplify = TRUE) - d <- data.frame(i = .jcall(result, "[I", "getIteration"), - entity = .jcall(result, "[S", "getEntities"), - backboneLoss = .jcall(result, "[D", "getBackboneLoss"), - redundantLoss = .jcall(result, "[D", "getRedundantLoss"), - statements = .jcall(result, "[I", "getNumStatements")) - rownames(d) <- NULL - attributes(d)$numStatementsFull <- .jcall(result, "I", "getNumStatementsFull") - attributes(d)$start <- as.POSIXct(.jcall(result, "J", "getStart"), origin = "1970-01-01") # add the start date/time of the result as an attribute - attributes(d)$stop <- as.POSIXct(.jcall(result, "J", "getStop"), origin = "1970-01-01") # add the end date/time of the result as an attribute - attributes(d)$method <- "nested" - class(d) <- c("dna_backbone", class(d)) - return(d) - } -} - -#' @rdname dna_backbone -#' @param x A \code{"dna_backbone"} object. -#' @param trim Number of maximum characters to display in entity labels. Labels -#' with more characters are truncated, and the last character is replaced by -#' an asterisk (\code{*}). -#' @export -print.dna_backbone <- function(x, trim = 50, ...) { - method <- attributes(x)$method - cat(paste0("Backbone method: ", method, ".\n\n")) - if (method %in% c("penalty", "fixed")) { - if (method == "penalty") { - cat(paste0("Penalty: ", x$penalty, ". Iterations: ", x$iterations, ".\n\n")) - } else { - cat(paste0("Backbone size: ", x$backbone_size, ". Iterations: ", x$iterations, ".\n\n")) - } - cat(paste0("Backbone set (loss: ", round(x$unpenalized_backbone_loss, 4), "):\n")) - cat(paste(1:length(x$backbone), x$backbone), sep = "\n") - cat(paste0("\nRedundant set (loss: ", round(x$unpenalized_redundant_loss, 4), "):\n")) - cat(paste(1:length(x$redundant), x$redundant), sep = "\n") - } else if (method == "nested") { - x2 <- x - x2$entity <- sapply(x2$entity, function(r) if (nchar(r) > trim) paste0(substr(r, 1, trim - 1), "*") else r) - print(as.data.frame(x2), row.names = FALSE) - } -} - -#' @param ma Number of iterations to compute moving average. -#' @rdname dna_backbone -#' @importFrom graphics lines -#' @importFrom stats filter -#' @importFrom rlang .data -#' @export -plot.dna_backbone <- function(x, ma = 500, ...) { - - if (attr(x, "method") != "nested") { - # temperature and acceptance probability - plot(x = x$diagnostics$iteration, - y = x$diagnostics$temperature, - col = "red", - type = "l", - lwd = 3, - xlab = "Iteration", - ylab = "Acceptance probability", - main = "Temperature and acceptance probability") - # note that better solutions are coded as -1 and need to be skipped: - lines(x = x$diagnostics$iteration[x$diagnostics$acceptance_prob >= 0], - y = x$diagnostics$acceptance_prob[x$diagnostics$acceptance_prob >= 0]) - - # spectral distance between full network and backbone network per iteration - bb_loss <- stats::filter(x$diagnostics$penalized_backbone_loss, - rep(1 / ma, ma), - sides = 1) - if (attributes(x)$method == "penalty") { - yl <- "Penalized backbone loss" - ti <- "Penalized spectral backbone distance" - } else { - yl <- "Backbone loss" - ti <- "Spectral backbone distance" - } - plot(x = x$diagnostics$iteration, - y = bb_loss, - type = "l", - xlab = "Iteration", - ylab = yl, - main = ti) - - # number of concepts in the backbone solution per iteration - current_size_ma <- stats::filter(x$diagnostics$current_backbone_size, - rep(1 / ma, ma), - sides = 1) - optimal_size_ma <- stats::filter(x$diagnostics$optimal_backbone_size, - rep(1 / ma, ma), - sides = 1) - plot(x = x$diagnostics$iteration, - y = current_size_ma, - ylim = c(min(c(current_size_ma, optimal_size_ma), na.rm = TRUE), - max(c(current_size_ma, optimal_size_ma), na.rm = TRUE)), - type = "l", - xlab = "Iteration", - ylab = paste0("Number of elements (MA, last ", ma, ")"), - main = "Backbone size (red = best)") - lines(x = x$diagnostics$iteration, y = optimal_size_ma, col = "red") - - # ratio of recent acceptances - accept_ratio <- stats::filter(x$diagnostics$acceptance, - rep(1 / ma, ma), - sides = 1) - plot(x = x$diagnostics$iteration, - y = accept_ratio, - type = "l", - xlab = "Iteration", - ylab = paste("Acceptance ratio in the last", ma, "iterations"), - main = "Acceptance ratio") - } else { # create hclust object - # define merging pattern: negative numbers are leaves, positive are merged clusters - merges_clust <- matrix(nrow = nrow(x) - 1, ncol = 2) - - merges_clust[1,1] <- -nrow(x) - merges_clust[1,2] <- -(nrow(x) - 1) - - for (i in 2:(nrow(x) - 1)) { - merges_clust[i, 1] <- -(nrow(x) - i) - merges_clust[i, 2] <- i - 1 - } - - # Initialize empty object - a <- list() - - # Add merges - a$merge <- merges_clust - - # Define merge heights - a$height <- x$backboneLoss[1:nrow(x) - 1] - - # Order of leaves - a$order <- 1:nrow(x) - - # Labels of leaves - a$labels <- rev(x$entity) - - # Define hclust class - class(a) <- "hclust" - - plot(a, ylab = "") - } -} - -#' @rdname dna_backbone -#' @param object A \code{"dna_backbone"} object. -#' @param ... Additional arguments. -#' @importFrom ggplot2 autoplot -#' @importFrom ggplot2 ggplot -#' @importFrom ggplot2 aes -#' @importFrom ggplot2 geom_line -#' @importFrom ggplot2 ylab -#' @importFrom ggplot2 xlab -#' @importFrom ggplot2 ggtitle -#' @importFrom ggplot2 theme_bw -#' @importFrom ggplot2 theme -#' @importFrom ggplot2 coord_flip -#' @importFrom ggplot2 scale_x_continuous -#' @importFrom ggplot2 scale_y_continuous -#' @importFrom rlang .data -#' @export -autoplot.dna_backbone <- function(object, ..., ma = 500) { - if (attr(object, "method") != "nested") { - bd <- object$diagnostics - bd$bb_loss <- stats::filter(bd$penalized_backbone_loss, rep(1 / ma, ma), sides = 1) - bd$current_size_ma <- stats::filter(bd$current_backbone_size, rep(1 / ma, ma), sides = 1) - bd$optimal_size_ma <- stats::filter(bd$optimal_backbone_size, rep(1 / ma, ma), sides = 1) - bd$accept_ratio <- stats::filter(bd$acceptance, rep(1 / ma, ma), sides = 1) - - # temperature and acceptance probability - g_accept <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["temperature"]], x = .data[["iteration"]])) + - ggplot2::geom_line(color = "#a50f15") + - ggplot2::geom_line(data = bd[bd$acceptance_prob >= 0, ], - ggplot2::aes(y = .data[["acceptance_prob"]], x = .data[["iteration"]])) + - ggplot2::ylab("Acceptance probability") + - ggplot2::xlab("Iteration") + - ggplot2::ggtitle("Temperature and acceptance probability") + - ggplot2::theme_bw() - - # spectral distance between full network and backbone network per iteration - if (attributes(object)$method == "penalty") { - yl <- "Penalized backbone loss" - ti <- "Penalized spectral backbone distance" - } else { - yl <- "Backbone loss" - ti <- "Spectral backbone distance" - } - g_loss <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["bb_loss"]], x = .data[["iteration"]])) + - ggplot2::geom_line() + - ggplot2::ylab(yl) + - ggplot2::xlab("Iteration") + - ggplot2::ggtitle(ti) + - ggplot2::theme_bw() - - # number of concepts in the backbone solution per iteration - d <- data.frame(iteration = rep(bd$iteration, 2), - size = c(bd$current_size_ma, bd$optimal_size_ma), - Criterion = c(rep("Current iteration", nrow(bd)), - rep("Best solution", nrow(bd)))) - g_size <- ggplot2::ggplot(d, ggplot2::aes(y = .data[["size"]], x = .data[["iteration"]], color = .data[["Criterion"]])) + - ggplot2::geom_line() + - ggplot2::ylab(paste0("Number of elements (MA, last ", ma, ")")) + - ggplot2::xlab("Iteration") + - ggplot2::ggtitle("Backbone size") + - ggplot2::theme_bw() + - ggplot2::theme(legend.position = "bottom") - - # ratio of recent acceptances - g_ar <- ggplot2::ggplot(bd, ggplot2::aes(y = .data[["accept_ratio"]], x = .data[["iteration"]])) + - ggplot2::geom_line() + - ggplot2::ylab(paste("Acceptance ratio in the last", ma, "iterations")) + - ggplot2::xlab("Iteration") + - ggplot2::ggtitle("Acceptance ratio") + - ggplot2::theme_bw() - - # wrap in list - plots <- list(g_accept, g_loss, g_size, g_ar) - return(plots) - } else { # create hclust object - # define merging pattern: negative numbers are leaves, positive are merged clusters - merges_clust <- matrix(nrow = nrow(object) - 1, ncol = 2) - - merges_clust[1,1] <- -nrow(object) - merges_clust[1,2] <- -(nrow(object) - 1) - - for (i in 2:(nrow(object) - 1)) { - merges_clust[i, 1] <- -(nrow(object) - i) - merges_clust[i, 2] <- i - 1 - } - - # Initialize empty object - a <- list() - - # Add merges - a$merge <- merges_clust - - # Define merge heights - a$height <- object$backboneLoss[1:nrow(object) - 1] - height <- a$height - - # Order of leaves - a$order <- 1:nrow(object) - - # Labels of leaves - a$labels <- rev(object$entity) - - # Define hclust class - class(a) <- "hclust" - - # ensure ggraph is installed, otherwise throw error (better than importing it to avoid hard dependency) - if (!requireNamespace("ggraph", quietly = TRUE)) { - stop("The 'ggraph' package is required for plotting nested backbone dendrograms with 'ggplot2' but was not found. Consider installing it.") - } - - g_clust <- ggraph::ggraph(graph = a, - layout = "dendrogram", - circular = FALSE, - height = height) + # TODO @Tim: "height" does not seem to exist - ggraph::geom_edge_elbow() + - ggraph::geom_node_point(aes(filter = .data[["leaf"]])) + - ggplot2::theme_bw() + - ggplot2::theme(panel.border = element_blank(), - axis.title = element_blank(), - panel.grid.major = element_blank(), - panel.grid.minor = element_blank(), - axis.line = element_blank(), - axis.text.y = element_text(size = 6), - axis.ticks.y = element_blank()) + - ggplot2::scale_x_continuous(breaks = seq(0, nrow(object) - 1, by = 1), - labels = rev(object$entity)) + - ggplot2::scale_y_continuous(expand = c(0, 0.01)) + - ggplot2::coord_flip() - - return(g_clust) - } -} - -#' @param backboneEntities A vector of character values to be included in the -#' backbone. The function will compute the spectral loss between the full -#' network and the network composed only of those entities on the second mode -#' that are contained in this vector. -#' @param p The penalty parameter. The default value of \code{0} means no -#' penalty for backbone size is applied. -#' @inheritParams dna_backbone -#' @return A vector with two numeric values: the backbone and redundant loss. -#' -#' @examples -#' \dontrun{ -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' dna_evaluateBackboneSolution( -#' c("There should be legislation to regulate emissions.", -#' "Emissions legislation should regulate CO2.") -#' ) -#' } -#' -#' @rdname dna_backbone -#' @importFrom rJava .jarray -#' @importFrom rJava .jcall -#' @importFrom rJava .jnull -#' @export -dna_evaluateBackboneSolution <- function(backboneEntities, - p = 0, - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "subtract", - normalization = "average", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE) { - - # wrap the vectors of exclude values for document variables into Java arrays - excludeAuthors <- .jarray(excludeAuthors) - excludeSources <- .jarray(excludeSources) - excludeSections <- .jarray(excludeSections) - excludeTypes <- .jarray(excludeTypes) - - # compile exclude variables and values vectors - dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) - count <- 0 - if (length(excludeValues) > 0) { - for (i in 1:length(excludeValues)) { - if (length(excludeValues[[i]]) > 0) { - for (j in 1:length(excludeValues[[i]])) { - count <- count + 1 - dat[count, 1] <- names(excludeValues)[i] - dat[count, 2] <- excludeValues[[i]][j] - } - } - } - var <- dat[, 1] - val <- dat[, 2] - } else { - var <- character() - val <- character() - } - var <- .jarray(var) # array of variable names of each excluded value - val <- .jarray(val) # array of values to be excluded - - # encode R NULL as Java null value if necessary - if (is.null(qualifier) || is.na(qualifier)) { - qualifier <- .jnull(class = "java/lang/String") - } - - # call rBackbone function to compute results - result <- .jcall(dnaEnvironment[["dna"]]$headlessDna, - "[D", - "rEvaluateBackboneSolution", - .jarray(backboneEntities), - as.integer(p), - statementType, - variable1, - variable1Document, - variable2, - variable2Document, - qualifier, - qualifierDocument, - qualifierAggregation, - normalization, - duplicates, - start.date, - stop.date, - start.time, - stop.time, - var, - val, - excludeAuthors, - excludeSources, - excludeSections, - excludeTypes, - invertValues, - invertAuthors, - invertSources, - invertSections, - invertTypes - ) - names(result) <- c("backbone loss", "redundant loss") - return(result) -} - - -# Clustering ------------------------------------------------------------------- - -#' Compute multiple cluster solutions for a discourse network -#' -#' Compute multiple cluster solutions for a discourse network. -#' -#' This function applies a number of different graph clustering techniques to -#' a discourse network dataset. The user provides many of the same arguments as -#' in the \code{\link{dna_network}} function and a few additional arguments that -#' determine which kinds of clustering methods should be used and how. In -#' particular, the \code{k} argument can be \code{0} (for arbitrary numbers of -#' clusters) or any positive integer value (e.g., \code{2}, for constraining the -#' number of clusters to exactly \code{k} groups). This is useful for assessing -#' the polarization of a discourse network. -#' -#' In particular, the function can be used to compute the maximal modularity of -#' a smoothed time series of discourse networks using the \code{timeWindow} and -#' \code{windowSize} arguments for a given \code{k} across a number of -#' clustering methods. -#' -#' It is also possible to switch off all but one clustering method using the -#' respective arguments and carry out a simple cluster analysis with the method -#' of choice for a certain time span of the discourse network, without any time -#' window options. -#' -#' @param saveObjects Store the original output of the respective clustering -#' method in the \code{cl} slot of the return object? If \code{TRUE}, one -#' cluster object per time point will be saved, for all time points for which -#' network data are available. At each time point, only the cluster object -#' with the highest modularity score will be saved, all others discarded. The -#' \code{max_mod} slot of the object contains additional information on which -#' measure was saved at each time point and what the corresponding modularity -#' score is. -#' @param k The number of clusters to compute. This constrains the choice of -#' clustering methods because some methods require a predefined \code{k} while -#' other methods do not. To permit arbitrary numbers of clusters, depending on -#' the respective algorithm (or the value of modularity in some cases), choose -#' \code{k = 0}. This corresponds to the theoretical notion of -#' "multipolarization". For "bipolarization", choose \code{k = 2} in order to -#' constrain the cluster solutions to exactly two groups. -#' @param k.max If \code{k = 0}, there can be arbitrary numbers of clusters. In -#' this case, \code{k.max} sets the maximal number of clusters that can be -#' identified. -#' @param single Include hierarchical clustering with single linkage in the pool -#' of clustering methods? The \code{\link[stats]{hclust}} function from -#' the \pkg{stats} package is applied to Jaccard distances in the affiliation -#' network for this purpose. Only valid if \code{k > 1}. -#' @param average Include hierarchical clustering with average linkage in the -#' pool of clustering methods? The \code{\link[stats]{hclust}} function from -#' the \pkg{stats} package is applied to Jaccard distances in the affiliation -#' network for this purpose. Only valid if \code{k > 1}. -#' @param complete Include hierarchical clustering with complete linkage in the -#' pool of clustering methods? The \code{\link[stats]{hclust}} function from -#' the \pkg{stats} package is applied to Jaccard distances in the affiliation -#' network for this purpose. Only valid if \code{k > 1}. -#' @param ward Include hierarchical clustering with Ward's algorithm in the -#' pool of clustering methods? The \code{\link[stats]{hclust}} function from -#' the \pkg{stats} package is applied to Jaccard distances in the affiliation -#' network for this purpose. If \code{k = 0} is selected, different solutions -#' with varying \code{k} are attempted, and the solution with the highest -#' modularity is retained. -#' @param kmeans Include k-means clustering in the pool of clustering methods? -#' The \code{\link[stats]{kmeans}} function from the \pkg{stats} package is -#' applied to Jaccard distances in the affiliation network for this purpose. -#' If \code{k = 0} is selected, different solutions with varying \code{k} are -#' attempted, and the solution with the highest modularity is retained. -#' @param pam Include partitioning around medoids in the pool of clustering -#' methods? The \code{\link[cluster]{pam}} function from the \pkg{cluster} -#' package is applied to Jaccard distances in the affiliation network for this -#' purpose. If \code{k = 0} is selected, different solutions with varying -#' \code{k} are attempted, and the solution with the highest modularity is -#' retained. -#' @param equivalence Include equivalence clustering (as implemented in the -#' \code{\link[sna]{equiv.clust}} function in the \pkg{sna} package), based on -#' shortest path distances between nodes (as implemented in the -#' \code{\link[sna]{sedist}} function in the \pkg{sna} package) in the -#' positive subtract network? If \code{k = 0} is selected, different solutions -#' with varying \code{k} are attempted, and the solution with the highest -#' modularity is retained. -#' @param concor_one Include CONvergence of iterative CORrelations (CONCOR) in -#' the pool of clustering methods? The algorithm is applied to the positive -#' subtract network to identify \code{k = 2} clusters. The method is omitted -#' if \code{k != 2}. -#' @param concor_two Include CONvergence of iterative CORrelations (CONCOR) in -#' the pool of clustering methods? The algorithm is applied to the affiliation -#' network to identify \code{k = 2} clusters. The method is omitted -#' if \code{k != 2}. -#' @param louvain Include the Louvain community detection algorithm in the pool -#' of clustering methods? The \code{\link[igraph]{cluster_louvain}} function -#' in the \pkg{igraph} package is applied to the positive subtract network for -#' this purpose. -#' @param fastgreedy Include the fast and greedy community detection algorithm -#' in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_fast_greedy}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param walktrap Include the Walktrap community detection algorithm -#' in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_walktrap}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param leading_eigen Include the leading eigenvector community detection -#' algorithm in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_leading_eigen}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param edge_betweenness Include the edge betweenness community detection -#' algorithm by Girvan and Newman in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_edge_betweenness}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param infomap Include the infomap community detection algorithm -#' in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_infomap}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param label_prop Include the label propagation community detection algorithm -#' in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_label_prop}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. -#' @param spinglass Include the spinglass community detection algorithm -#' in the pool of clustering methods? The -#' \code{\link[igraph]{cluster_spinglass}} function in the \pkg{igraph} -#' package is applied to the positive subtract network for this purpose. Note -#' that this method is disabled by default because it is relatively slow. -#' @inheritParams dna_network -#' -#' @return The function creates a \code{dna_multiclust} object, which contains -#' the following items: -#' \describe{ -#' \item{k}{The number of clusters determined by the user.} -#' \item{cl}{Cluster objects returned by the respective cluster function. If -#' multiple methods are used, this returns the object with the highest -#' modularity.} -#' \item{max_mod}{A data frame with one row per time point (that is, only one -#' row in the default case and multiple rows if time windows are used) and -#' the maximal modularity for the given time point across all cluster -#' methods.} -#' \item{modularity}{A data frame with the modularity values for all separate -#' cluster methods and all time points.} -#' \item{membership}{A large data frame with all nodes' membership information -#' for each time point and each clustering method.} -#' } -#' -#' @author Philip Leifeld -#' -#' @examples -#' \dontrun{ -#' library("rDNA") -#' dna_init() -#' samp <- dna_sample() -#' dna_openDatabase(samp, coderId = 1, coderPassword = "sample") -#' -#' # example 1: compute 12 cluster solutions for one time point -#' mc1 <- dna_multiclust(variable1 = "organization", -#' variable2 = "concept", -#' qualifier = "agreement", -#' duplicates = "document", -#' k = 0, # flexible numbers of clusters -#' saveObjects = TRUE) # retain hclust object -#' -#' mc1$modularity # return modularity scores for 12 clustering methods -#' mc1$max_mod # return the maximal value of the 12, along with dates -#' mc1$memberships # return cluster memberships for all 12 cluster methods -#' plot(mc1$cl[[1]]) # plot hclust dendrogram -#' -#' # example 2: compute only Girvan-Newman edge betweenness with two clusters -#' set.seed(12345) -#' mc2 <- dna_multiclust(k = 2, -#' single = FALSE, -#' average = FALSE, -#' complete = FALSE, -#' ward = FALSE, -#' kmeans = FALSE, -#' pam = FALSE, -#' equivalence = FALSE, -#' concor_one = FALSE, -#' concor_two = FALSE, -#' louvain = FALSE, -#' fastgreedy = FALSE, -#' walktrap = FALSE, -#' leading_eigen = FALSE, -#' edge_betweenness = TRUE, -#' infomap = FALSE, -#' label_prop = FALSE, -#' spinglass = FALSE) -#' mc2$memberships # return membership in two clusters -#' mc2$modularity # return modularity of the cluster solution -#' -#' # example 3: smoothed modularity using time window algorithm -#' mc3 <- dna_multiclust(k = 2, -#' timeWindow = "events", -#' windowSize = 28) -#' mc3$max_mod # maximal modularity and method per time point -#' } -#' -#' @rdname dna_multiclust -#' @importFrom stats as.dist cor hclust cutree kmeans -#' @export -dna_multiclust <- function(statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - duplicates = "include", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - timeWindow = "no", - windowSize = 100, - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE, - saveObjects = FALSE, - k = 0, - k.max = 5, - single = TRUE, - average = TRUE, - complete = TRUE, - ward = TRUE, - kmeans = TRUE, - pam = TRUE, - equivalence = TRUE, - concor_one = TRUE, - concor_two = TRUE, - louvain = TRUE, - fastgreedy = TRUE, - walktrap = TRUE, - leading_eigen = TRUE, - edge_betweenness = TRUE, - infomap = TRUE, - label_prop = TRUE, - spinglass = FALSE) { - - # check dependencies - if (!requireNamespace("igraph", quietly = TRUE)) { # version 0.8.1 required for edge betweenness to work fine. - stop("The 'dna_multiclust' function requires the 'igraph' package to be installed.\n", - "To do this, enter 'install.packages(\"igraph\")'.") - } else if (packageVersion("igraph") < "0.8.1" && edge_betweenness) { - warning("Package version of 'igraph' < 0.8.1. If edge betweenness algorithm encounters an empty network matrix, this will let R crash. See here: https://github.com/igraph/rigraph/issues/336. Consider updating 'igraph' to the latest version.") - } - if (pam && !requireNamespace("cluster", quietly = TRUE)) { - pam <- FALSE - warning("Argument 'pam = TRUE' requires the 'cluster' package, which is not installed.\nSetting 'pam = FALSE'. Consider installing the 'cluster' package.") - } - if (equivalence && !requireNamespace("sna", quietly = TRUE)) { - equivalence <- FALSE - warning("Argument 'equivalence = TRUE' requires the 'sna' package, which is not installed.\nSetting 'equivalence = FALSE'. Consider installing the 'sna' package.") - } - - # check argument validity - if (is.null(k) || is.na(k) || !is.numeric(k) || length(k) > 1 || is.infinite(k) || k < 0) { - stop("'k' must be a non-negative integer number. Can be 0 for flexible numbers of clusters.") - } - if (is.null(k.max) || is.na(k.max) || !is.numeric(k.max) || length(k.max) > 1 || is.infinite(k.max) || k.max < 1) { - stop("'k.max' must be a positive integer number.") - } - if (k == 1) { - k <- 0 - warning("'k' must be 0 (for arbitrary numbers of clusters) or larger than 1 (to constrain number of clusters). Using 'k = 0'.") - } - - # determine what kind of two-mode network to create - if (is.null(qualifier) || is.na(qualifier) || !is.character(qualifier)) { - qualifierAggregation <- "ignore" - } else { - v <- dna_getVariables(statementType = statementType) - if (v$type[v$label == qualifier] == "boolean") { - qualifierAggregation <- "combine" - } else { - qualifierAggregation <- "subtract" - } - } - - nw_aff <- dna_network(networkType = "twomode", - statementType = statementType, - variable1 = variable1, - variable1Document = variable1Document, - variable2 = variable2, - variable2Document = variable2Document, - qualifier = qualifier, - qualifierAggregation = qualifierAggregation, - normalization = "no", - duplicates = duplicates, - start.date = start.date, - stop.date = stop.date, - start.time = start.time, - stop.time = stop.time, - timeWindow = timeWindow, - windowSize = windowSize, - excludeValues = excludeValues, - excludeAuthors = excludeAuthors, - excludeSources = excludeSources, - excludeSections = excludeSections, - excludeTypes = excludeTypes, - invertValues = invertValues, - invertAuthors = invertAuthors, - invertSources = invertSources, - invertSections = invertSections, - invertTypes = invertTypes) - nw_sub <- dna_network(networkType = "onemode", - statementType = statementType, - variable1 = variable1, - variable1Document = variable1Document, - variable2 = variable2, - variable2Document = variable2Document, - qualifier = qualifier, - qualifierAggregation = "subtract", - normalization = "average", - duplicates = duplicates, - start.date = start.date, - stop.date = stop.date, - start.time = start.time, - stop.time = stop.time, - timeWindow = timeWindow, - windowSize = windowSize, - excludeValues = excludeValues, - excludeAuthors = excludeAuthors, - excludeSources = excludeSources, - excludeSections = excludeSections, - excludeTypes = excludeTypes, - invertValues = invertValues, - invertAuthors = invertAuthors, - invertSources = invertSources, - invertSections = invertSections, - invertTypes = invertTypes) - - if (timeWindow == "no") { - dta <- list() - dta$networks <- list(nw_sub) - nw_sub <- dta - dta <- list() - dta$networks <- list(nw_aff) - nw_aff <- dta - } - - obj <- list() - if (isTRUE(saveObjects)) { - obj$cl <- list() - } - dta_dat <- list() - dta_mem <- list() - dta_mod <- list() - counter <- 1 - if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { - num_networks <- length(nw_sub) - } else { - num_networks <- 1 - } - for (i in 1:num_networks) { - - # prepare dates - if (timeWindow == "no") { - dta_dat[[i]] <- data.frame(i = i, - start = attributes(nw_sub$networks[[i]])$start, - stop = attributes(nw_sub$networks[[i]])$stop) - } else { - dta_dat[[i]] <- data.frame(i = i, - start.date = attributes(nw_sub[[i]])$start, - middle.date = attributes(nw_sub[[i]])$middle, - stop.date = attributes(nw_sub[[i]])$stop) - } - - # prepare two-mode network - if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { - x <- nw_aff[[i]] - } else { - x <- nw_aff$networks[[i]] - } - if (qualifierAggregation == "combine") { - combined <- cbind(apply(x, 1:2, function(x) ifelse(x %in% c(1, 3), 1, 0)), - apply(x, 1:2, function(x) ifelse(x %in% c(2, 3), 1, 0))) - } else { - combined <- x - } - combined <- combined[rowSums(combined) > 0, , drop = FALSE] - rn <- rownames(combined) - - # Jaccard distances for two-mode network (could be done using vegdist function in vegan package, but saving the dependency) - combined <- matrix(as.integer(combined > 0), nrow = nrow(combined)) # probably not necessary, but ensure it's an integer matrix - intersections <- tcrossprod(combined) # compute intersections using cross-product - row_sums <- rowSums(combined) # compute row sums - unions <- matrix(outer(row_sums, row_sums, `+`), ncol = length(row_sums)) - intersections # compute unions - jaccard_similarities <- intersections / unions # calculate Jaccard similarities - jaccard_similarities[is.nan(jaccard_similarities)] <- 0 # avoid division by zero - jaccard_distances <- 1 - jaccard_similarities # convert to Jaccard distances - rownames(jaccard_distances) <- rn # re-attach the row names - jac <- stats::as.dist(jaccard_distances) # convert to dist object - - # prepare one-mode network - if ("dna_network_onemode_timewindows" %in% class(nw_sub)) { - y <- nw_sub[[i]] - } else { - y <- nw_sub$networks[[i]] - } - y[y < 0] <- 0 - class(y) <- "matrix" - g <- igraph::graph_from_adjacency_matrix(y, mode = "undirected", weighted = TRUE) - - if (nrow(combined) > 1) { - counter_current <- 1 - current_cl <- list() - current_mod <- numeric() - - # Hierarchical clustering with single linkage - if (isTRUE(single) && k > 1) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "single")) - mem <- stats::cutree(cl, k = k) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Single)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Single)", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with single linkage with optimal k - if (isTRUE(single) && k < 2) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "single")) - opt_k <- lapply(2:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Single)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Single)", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with average linkage - if (isTRUE(average) && k > 1) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "average")) - mem <- stats::cutree(cl, k = k) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Average)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Average)", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with average linkage with optimal k - if (isTRUE(average) && k < 2) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "average")) - opt_k <- lapply(2:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Average)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Average)", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with complete linkage - if (isTRUE(complete) && k > 1) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "complete")) - mem <- stats::cutree(cl, k = k) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Complete)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Complete)", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with complete linkage with optimal k - if (isTRUE(complete) && k < 2) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "complete")) - opt_k <- lapply(2:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Complete)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Complete)", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with the Ward algorithm - if (isTRUE(ward) && k > 1) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "ward.D2")) - mem <- stats::cutree(cl, k = k) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Ward)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Ward)", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Hierarchical clustering with the Ward algorithm with optimal k - if (isTRUE(ward) && k < 2) { - try({ - suppressWarnings(cl <- stats::hclust(jac, method = "ward.D2")) - opt_k <- lapply(2:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Hierarchical (Ward)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Hierarchical (Ward)", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # k-means - if (isTRUE(kmeans) && k > 1) { - try({ - suppressWarnings(cl <- stats::kmeans(jac, centers = k)) - mem <- cl$cluster - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("k-Means", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "k-Means", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # k-means with optimal k - if (isTRUE(kmeans) && k < 2) { - try({ - opt_k <- lapply(2:k.max, function(x) { - suppressWarnings(cl <- stats::kmeans(jac, centers = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("k-Means", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "k-Means", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - cl <- opt_k[[kk]]$cl - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # pam - if (isTRUE(pam) && k > 1) { - try({ - suppressWarnings(cl <- cluster::pam(jac, k = k)) - mem <- cl$cluster - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Partitioning around Medoids", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Partitioning around Medoids", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # pam with optimal k - if (isTRUE(pam) && k < 2) { - try({ - opt_k <- lapply(2:k.max, function(x) { - suppressWarnings(cl <- cluster::pam(jac, k = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Partitioning around Medoids", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Partitioning around Medoids", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - cl <- opt_k[[kk]]$cl - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Equivalence clustering - if (isTRUE(equivalence) && k > 1) { - try({ - suppressWarnings(cl <- sna::equiv.clust(y, equiv.dist = sna::sedist(y, method = "euclidean"))) - mem <- stats::cutree(cl$cluster, k = k) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Equivalence", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Equivalence", - k = k, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Equivalence clustering with optimal k - if (isTRUE(equivalence) && k < 2) { - try({ - suppressWarnings(cl <- sna::equiv.clust(y, equiv.dist = sna::sedist(y, method = "euclidean"))) - opt_k <- lapply(2:k.max, function(x) { - mem <- stats::cutree(cl$cluster, k = x) - mod <- igraph::modularity(x = g, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mod <- max(mod) - mem <- opt_k[[kk]]$mem - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Equivalence", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Equivalence", - k = kk + 1, # add one because the series started with k = 2 - modularity = mod, - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # CONCOR based on the positive subtract network - if (isTRUE(concor_one) && k %in% c(0, 2)) { - try({ - suppressWarnings(mi <- stats::cor(y)) - iter <- 1 - while (any(abs(mi) <= 0.999) & iter <= 50) { - mi[is.na(mi)] <- 0 - mi <- stats::cor(mi) - iter <- iter + 1 - } - mem <- ((mi[, 1] > 0) * 1) + 1 - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("CONCOR (One-Mode)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "CONCOR (One-Mode)", - k = 2, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- mem - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # CONCOR based on the combined affiliation network - if (isTRUE(concor_two) && k %in% c(0, 2)) { - try({ - suppressWarnings(mi <- stats::cor(t(combined))) - iter <- 1 - while (any(abs(mi) <= 0.999) & iter <= 50) { - mi[is.na(mi)] <- 0 - mi <- stats::cor(mi) - iter <- iter + 1 - } - mem <- ((mi[, 1] > 0) * 1) + 1 - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("CONCOR (Two-Mode)", length(mem)), - node = rownames(x), - cluster = mem, - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "CONCOR (Two-Mode)", - k = 2, - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- mem - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Louvain clustering - if (isTRUE(louvain) && k < 2) { - try({ - suppressWarnings(cl <- igraph::cluster_louvain(g)) - mem <- igraph::membership(cl) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Louvain", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Louvain", - k = max(as.numeric(mem)), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Fast & Greedy community detection (with or without cut) - if (isTRUE(fastgreedy)) { - try({ - suppressWarnings(cl <- igraph::cluster_fast_greedy(g, merges = TRUE)) - if (k == 0) { - mem <- igraph::membership(cl) - } else { - mem <- suppressWarnings(igraph::cut_at(cl, no = k)) - if ((k + 1) %in% as.numeric(mem)) { - stop() - } - } - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Fast & Greedy", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Fast & Greedy", - k = ifelse(k == 0, max(as.numeric(mem)), k), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Walktrap community detection (with or without cut) - if (isTRUE(walktrap)) { - try({ - suppressWarnings(cl <- igraph::cluster_walktrap(g, merges = TRUE)) - if (k == 0) { - mem <- igraph::membership(cl) - } else { - mem <- suppressWarnings(igraph::cut_at(cl, no = k)) - if ((k + 1) %in% as.numeric(mem)) { - stop() - } - } - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Walktrap", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Walktrap", - k = ifelse(k == 0, max(as.numeric(mem)), k), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Leading Eigenvector community detection (only without cut) - if (isTRUE(leading_eigen) && k < 2) { # it *should* work with cut_at because is.hierarchical(cl) returns TRUE, but it never works... - try({ - suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) - mem <- igraph::membership(cl) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Leading Eigenvector", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Leading Eigenvector", - k = max(as.numeric(mem)), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Edge Betweenness community detection (with or without cut) - if (isTRUE(edge_betweenness)) { - try({ - suppressWarnings(cl <- igraph::cluster_edge_betweenness(g, merges = TRUE)) - if (k == 0) { - mem <- igraph::membership(cl) - } else { - mem <- suppressWarnings(igraph::cut_at(cl, no = k)) - if ((k + 1) %in% as.numeric(mem)) { - stop() - } - } - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Edge Betweenness", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Edge Betweenness", - k = ifelse(k == 0, max(as.numeric(mem)), k), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Infomap community detection - if (isTRUE(infomap) && k < 2) { - try({ - suppressWarnings(cl <- igraph::cluster_infomap(g)) - mem <- igraph::membership(cl) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Infomap", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Infomap", - k = max(as.numeric(mem)), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Label Propagation community detection - if (isTRUE(label_prop) && k < 2) { - try({ - suppressWarnings(cl <- igraph::cluster_label_prop(g)) - mem <- igraph::membership(cl) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Label Propagation", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Label Propagation", - k = max(as.numeric(mem)), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # Spinglass community detection - if (isTRUE(spinglass) && k < 2) { - try({ - suppressWarnings(cl <- igraph::cluster_spinglass(g)) - mem <- igraph::membership(cl) - dta_mem[[counter]] <- data.frame(i = rep(i, length(mem)), - method = rep("Spinglass", length(mem)), - node = rownames(x), - cluster = as.numeric(mem), - stringsAsFactors = FALSE) - dta_mod[[counter]] <- data.frame(i = i, - method = "Spinglass", - k = max(as.numeric(mem)), - modularity = igraph::modularity(x = g, membership = mem), - stringsAsFactors = FALSE) - if (isTRUE(saveObjects)) { - current_cl[[counter_current]] <- cl - current_mod[counter_current] <- dta_mod[[counter]]$modularity[nrow(dta_mod[[counter]])] - counter_current <- counter_current + 1 - } - counter <- counter + 1 - }, silent = TRUE) - } - - # retain cluster object where modularity was maximal - if (isTRUE(saveObjects) && length(current_cl) > 0) { - obj$cl[[i]] <- current_cl[[which.max(current_mod)]] - } - } - } - obj$cl <- obj$cl[!sapply(obj$cl, is.null)] # remove NULL objects that may occur when the network is empty - obj$k <- k - obj$max_mod <- do.call(rbind, dta_dat) - memberships <- do.call(rbind, dta_mem) - rownames(memberships) <- NULL - obj$memberships <- memberships - obj$modularity <- do.call(rbind, dta_mod) - if (nrow(obj$modularity) == 0) { - stop("No output rows. Either you switched all clustering methods off, or all methods you used produced errors.") - } - obj$max_mod <- obj$max_mod[obj$max_mod$i %in% obj$modularity$i, ] # remove date entries where the network is empty - obj$max_mod$max_mod <- sapply(obj$max_mod$i, function(x) max(obj$modularity$modularity[obj$modularity$i == x], na.rm = TRUE)) # attach max_mod to $max_mod - # attach max_method to $max_mod - obj$max_mod$max_method <- sapply(obj$max_mod$i, - function(x) obj$modularity$method[obj$modularity$i == x & obj$modularity$modularity == max(obj$modularity$modularity[obj$modularity$i == x], na.rm = TRUE)][1]) - # attach k to max_mod - obj$max_mod$k <- sapply(obj$max_mod$i, function(x) max(obj$modularity$k[obj$modularity$i == x], na.rm = TRUE)) - - # diagnostics - if (isTRUE(single) && !"Hierarchical (Single)" %in% obj$modularity$method && k > 1) { - warning("'single' omitted due to an unknown problem.") - } - if (isTRUE(average) && !"Hierarchical (Average)" %in% obj$modularity$method && k > 1) { - warning("'average' omitted due to an unknown problem.") - } - if (isTRUE(complete) && !"Hierarchical (Complete)" %in% obj$modularity$method && k > 1) { - warning("'complete' omitted due to an unknown problem.") - } - if (isTRUE(ward) && !"Hierarchical (Ward)" %in% obj$modularity$method) { - warning("'ward' omitted due to an unknown problem.") - } - if (isTRUE(kmeans) && !"k-Means" %in% obj$modularity$method) { - warning("'kmeans' omitted due to an unknown problem.") - } - if (isTRUE(pam) && !"Partitioning around Medoids" %in% obj$modularity$method) { - warning("'pam' omitted due to an unknown problem.") - } - if (isTRUE(equivalence) && !"Equivalence" %in% obj$modularity$method) { - warning("'equivalence' omitted due to an unknown problem.") - } - if (isTRUE(concor_one) && !"CONCOR (One-Mode)" %in% obj$modularity$method && k %in% c(0, 2)) { - warning("'concor_one' omitted due to an unknown problem.") - } - if (isTRUE(concor_two) && !"CONCOR (Two-Mode)" %in% obj$modularity$method && k %in% c(0, 2)) { - warning("'concor_two' omitted due to an unknown problem.") - } - if (isTRUE(louvain) && !"Louvain" %in% obj$modularity$method && k < 2) { - warning("'louvain' omitted due to an unknown problem.") - } - if (isTRUE(fastgreedy) && !"Fast & Greedy" %in% obj$modularity$method) { - warning("'fastgreedy' omitted due to an unknown problem.") - } - if (isTRUE(walktrap) && !"Walktrap" %in% obj$modularity$method) { - warning("'walktrap' omitted due to an unknown problem.") - } - if (isTRUE(leading_eigen) && !"Leading Eigenvector" %in% obj$modularity$method && k < 2) { - warning("'leading_eigen' omitted due to an unknown problem.") - } - if (isTRUE(edge_betweenness) && !"Edge Betweenness" %in% obj$modularity$method) { - warning("'edge_betweenness' omitted due to an unknown problem.") - } - if (isTRUE(infomap) && !"Infomap" %in% obj$modularity$method && k < 2) { - warning("'infomap' omitted due to an unknown problem.") - } - if (isTRUE(label_prop) && !"Label Propagation" %in% obj$modularity$method && k < 2) { - warning("'label_prop' omitted due to an unknown problem.") - } - if (isTRUE(spinglass) && !"Spinglass" %in% obj$modularity$method && k < 2) { - warning("'spinglass' omitted due to an unknown problem.") - } - - class(obj) <- "dna_multiclust" - return(obj) -} - -#' Print the summary of a \code{dna_multiclust} object -#' -#' Show details of a \code{dna_multiclust} object. -#' -#' Print abbreviated contents for the slots of a \code{dna_multiclust} object, -#' which can be created using the \link{dna_multiclust} function. -#' -#' @param x A \code{dna_multiclust} object. -#' @param ... Further options (currently not used). -#' -#' @author Philip Leifeld -#' -#' @rdname dna_multiclust -#' @importFrom utils head -#' @export -print.dna_multiclust <- function(x, ...) { - cat(paste0("$k\n", x$k, "\n")) - if ("cl" %in% names(x)) { - cat(paste0("\n$cl\n", length(x$cl), " cluster object(s) embedded.\n")) - } - cat("\n$max_mod\n") - print(utils::head(x$max_mod)) - if (nrow(x$max_mod) > 6) { - cat(paste0("[... ", nrow(x$max_mod), " rows]\n")) - } - cat("\n$modularity\n") - print(utils::head(x$modularity)) - if (nrow(x$modularity) > 6) { - cat(paste0("[... ", nrow(x$modularity), " rows]\n")) - } - cat("\n$memberships\n") - print(utils::head(x$memberships)) - if (nrow(x$memberships) > 6) { - cat(paste0("[... ", nrow(x$memberships), " rows]\n")) - } -} - - -# Phase transitions ------------------------------------------------------------ - -#' Detect phase transitions and states in a discourse network -#' -#' Detect phase transitions and states in a discourse network. -#' -#' This function applies the state dynamics methods of Masuda and Holme to a -#' time window discourse network. It computes temporally overlapping discourse -#' networks, computes the dissimilarity between all networks, and clusters them. -#' For the dissimilarity, the sum of absolute edge weight differences and the -#' Euclidean spectral distance are available. Several clustering techniques can -#' be applied to identify the different stages and phases from the resulting -#' distance matrix. -#' -#' The function offers kernel smoothing, which means the farther away from a -#' time point a statement is, the less important it becomes for the network that -#' is created around the time point. Several kernel smoothing functions are -#' available; see the \code{kernel} argument. -#' -#' @param distanceMethod The distance measure that expresses the dissimilarity -#' between any two network matrices. The following choices are available: -#' \itemize{ -#' \item \code{"absdiff"}: The sum of the cell-wise absolute differences -#' between the two matrices, i.e., the sum of differences in edge weights. -#' This is equivalent to the graph edit distance because the network -#' dimensions are kept constant across all networks by including all nodes -#' at all time points (i.e., by including isolates). -#' \item \code{"spectral"}: The Euclidean distance between the normalized -#' eigenvalues of the graph Laplacian matrices, also called the spectral -#' distance between two network matrices. Any negative values (e.g., from -#' the subtract method) are replaced by zero before computing the -#' distance. -#' } -#' @param clusterMethods The clustering techniques that are applied to the -#' distance matrix in the end. Hierarchical methods are repeatedly cut off at -#' different levels, and solutions are compared using network modularity to -#' pick the best-fitting cluster membership vector. Some of the methods are -#' slower than others, hence they are not included by default. It is possible -#' to include any number of methods in the argument. For each included method, -#' the cluster membership vector (i.e., the states over time) along with the -#' associated time stamps of the networks are returned, and the modularity of -#' each included method is computed for comparison. The following methods are -#' available: -#' \itemize{ -#' \item \code{"single"}: Hierarchical clustering with single linkage using -#' the \code{\link[stats]{hclust}} function from the \pkg{stats} package. -#' \item \code{"average"}: Hierarchical clustering with average linkage -#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} -#' package. -#' \item \code{"complete"}: Hierarchical clustering with complete linkage -#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} -#' package. -#' \item \code{"ward"}: Hierarchical clustering with Ward's method (D2) -#' using the \code{\link[stats]{hclust}} function from the \pkg{stats} -#' package. -#' \item \code{"kmeans"}: k-means clustering using the -#' \code{\link[stats]{kmeans}} function from the \pkg{stats} package. -#' \item \code{"pam"}: Partitioning around medoids using the -#' \code{\link[cluster]{pam}} function from the \pkg{cluster} package. -#' \item \code{"spectral"}: Spectral clustering. An affinity matrix using a -#' Gaussian (RBF) kernel is created. The Laplacian matrix of the affinity -#' matrix is computed and normalized. The first first k eigenvectors of -#' the normalized Laplacian matrix are clustered using k-means. -#' \item \code{"concor"}: CONvergence of iterative CORrelations (CONCOR) -#' with exactly \code{k = 2} clusters. (Not included by default because of -#' the limit to \code{k = 2}.) -#' \item \code{"fastgreedy"}: Fast & greedy community detection using the -#' \code{\link[igraph]{cluster_fast_greedy}} function in the \pkg{igraph} -#' package. -#' \item \code{"walktrap"}: Walktrap community detection using the -#' \code{\link[igraph]{cluster_walktrap}} function in the \pkg{igraph} -#' package. -#' \item \code{"leading_eigen"}: Leading eigenvector community detection -#' using the \code{\link[igraph]{cluster_leading_eigen}} function in the -#' \pkg{igraph} package. (Can be slow, hence not included by default.) -#' \item \code{"edge_betweenness"}: Girvan-Newman edge betweenness community -#' detection using the \code{\link[igraph]{cluster_edge_betweenness}} -#' function in the \pkg{igraph} package. (Can be slow, hence not included -#' by default.) -#' } -#' @param k.min For the hierarchical cluster methods, how many clusters or -#' states should at least be identified? Only the best solution between -#' \code{k.min} and \code{k.max} clusters is retained and compared to other -#' methods. -#' @param k.max For the hierarchical cluster methods, up to how many clusters or -#' states should be identified? Only the best solution between \code{k.min} -#' and \code{k.max} clusters is retained and compared to other methods. -#' @param cores The number of computing cores for parallel processing. If -#' \code{1} (the default), no parallel processing is used. If a larger number, -#' the \pkg{pbmcapply} package is used to parallelize the clustering. Note -#' that this method is based on forking and is only available on Unix -#' operating systems, including MacOS and Linux. Note also that the remaining -#' computations, including the computation of the distance matrix and the -#' time window network generation with kernel smoothing, are done in parallel -#' using threads in Java, irrespective of this setting, using as many parallel -#' threads as cores are available on the system. -#' @param kernel Use kernel smoothing for computing network time slices? Several -#' kernel smoothing functions are available, similar to kernel density -#' estimation. They down-weight statements the farther they are temporally -#' away from the temporal mid-point of the respective time slice. Valid -#' settings are: -#' \itemize{ -#' \item \code{"uniform"}: Weight all statements within a time window -#' equally with a value of \code{0.5}. -#' \item \code{"triangular"}: Use a triangular kernel function. -#' \item \code{"epanechnikov"}: Use an Epanechnikov kernel smoother. -#' \item \code{"gaussian"}: Use a standard normal distribution as a kernel -#' smoother. -#' \item \code{"no"}: Circumvent kernel smoothing and weight all statements -#' with a value of \code{1.0}. This is a legacy setting and is slow and -#' may not return the same results as \code{"uniform"} due to the way it -#' was written up. -#' } -#' @param normalizeToOne Divide all cells by the sum of all cells before -#' computing the dissimilarity between two network matrices? This -#' normalization scales all edge weights to a sum of \code{1.0}. Doing so can -#' make networks more comparable by boosting the edge weights of networks that -#' are relatively sparsely populated by concepts, for example at the beginning -#' or end of the debate. Note that this normalization should not make any -#' difference with Euclidean spectral distances of the graph Laplacian because -#' the eigenvalues are normalized to sum to one in this distance method. -#' @param indentTime If \code{TRUE}, the sequence of time slices under the time -#' window algorithm starts with the first network and ends with the last -#' network that are entirely covered within the timeline defined by the start -#' and stop dates and times. For example, if the start date is 1 February, the -#' stop date is 31 December, and the time window duration is 21 days, the -#' mid-point of the first time window will be 11 February (to ensure the first -#' network entirely fits into the timeline), and the last network will be -#' centered around 20 December (to ensure the last network entirely fits into -#' the timeline). If \code{FALSE}, the start and stop dates and times are used -#' as the first and last mid-points. In that case, the first and last few -#' networks may contain fewer statements than other time slices and may, -#' therefore, be more similar to each other. This can potentially be -#' counter-acted by setting the \code{normalizeToOne} argument. -#' @inheritParams dna_network -#' -#' @examples -#' \dontrun{ -#' library("ggplot2") -#' dna_init() -#' dna_sample() -#' dna_openDatabase("sample.dna", coderId = 1, coderPassword = "sample") -#' -#' # compute states and phases for sample dataset -#' results <- dna_phaseTransitions(distanceMethod = "spectral", -#' clusterMethods = c("ward", -#' "pam", -#' "concor", -#' "walktrap"), -#' k.min = 2, -#' k.max = 6, -#' networkType = "onemode", -#' variable1 = "organization", -#' variable2 = "concept", -#' timeWindow = "days", -#' windowSize = 15, -#' kernel = "gaussian", -#' indentTime = FALSE, -#' normalizeToOne = FALSE) -#' results -#' autoplot(results) -#' -#' # access individual plots -#' plots <- autoplot(results) -#' plots[[1]] # show heatmap -#' plots[[2]] # show cluster silhouettes -#' plots[[3]] # show temporal embedding -#' plots[[4]] # show state dynamics -#' -#' # save plots to combined PDF -#' library("ggplotify") # needed to convert heatmap to ggplot diagram -#' library("patchwork") # needed to merge plots into 4 x 4 diagram -#' p1 <- ggplotify::as.ggplot(plots[[1]]) -#' p <- p1 + plots[[2]] + plots[[3]] + plots[[4]] + plot_layout(ncol = 2) -#' ggsave(filename = "phase_transitions.pdf", p, width = 14, height = 12) -#' } -#' -#' @rdname dna_phaseTransitions -#' @author Philip Leifeld -#' @importFrom stats dist -#' @importFrom utils combn -#' @importFrom rJava .jarray .jcall .jnull J -#' @export -dna_phaseTransitions <- function(distanceMethod = "absdiff", - clusterMethods = c("single", - "average", - "complete", - "ward", - "kmeans", - "pam", - "spectral", - "fastgreedy", - "walktrap"), - k.min = 2, - k.max = 6, - cores = 1, - networkType = "twomode", - statementType = "DNA Statement", - variable1 = "organization", - variable1Document = FALSE, - variable2 = "concept", - variable2Document = FALSE, - qualifier = "agreement", - qualifierDocument = FALSE, - qualifierAggregation = "subtract", - normalization = "no", - duplicates = "document", - start.date = "01.01.1900", - stop.date = "31.12.2099", - start.time = "00:00:00", - stop.time = "23:59:59", - timeWindow = "days", - windowSize = 200, - kernel = "uniform", - normalizeToOne = TRUE, - indentTime = FALSE, - excludeValues = list(), - excludeAuthors = character(), - excludeSources = character(), - excludeSections = character(), - excludeTypes = character(), - invertValues = FALSE, - invertAuthors = FALSE, - invertSources = FALSE, - invertSections = FALSE, - invertTypes = FALSE) { - - # check arguments and packages - if (distanceMethod == "spectral" && networkType == "twomode") { - distanceMethod <- "absdiff" - warning("Spectral distances only work with one-mode networks. Using 'distanceMethod = \"absdiff\"' instead.") - } - if (cores > 1 && !requireNamespace("pbmcapply", quietly = TRUE)) { - pbmclapply <- FALSE - warning("Argument 'cores' requires the 'pbmcapply' package, which is not installed.\nSetting 'cores = 1'. Consider installing the 'pbmcapply' package if you use Linux or MacOS.") - } - igraphMethods <- c("louvain", "fastgreedy", "walktrap", "leading_eigen", "edge_betweenness", "infomap", "label_prop", "spinglass") - if (any(igraphMethods %in% clusterMethods) && !requireNamespace("igraph", quietly = TRUE)) { - clusterMethods <- clusterMethods[-igraphMethods] - warning("'igraph' package not installed. Dropping clustering methods from the 'igraph' package. Consider installing 'igraph'.") - } - if ("pam" %in% clusterMethods && !requireNamespace("cluster", quietly = TRUE)) { - clusterMethods <- clusterMethods[which(clusterMethods != "pam")] - warning("'cluster' package not installed. Dropping clustering methods from the 'cluster' package. Consider installing 'cluster'.") - } - if ("concor" %in% clusterMethods && k.min > 2) { - clusterMethods <- clusterMethods[which(clusterMethods != "concor")] - warning("Dropping 'concor' from clustering methods because the CONCOR implementation in rDNA can only find exactly two clusters, but the 'k.min' argument was larger than 2.") - } - clusterMethods <- rev(clusterMethods) # reverse order to save time during parallel computation by starting the computationally intensive methods first - mcall <- match.call() # save the arguments for storing them in the results later - - # generate the time window networks - if (is.null(timeWindow) || is.na(timeWindow) || !is.character(timeWindow) || length(timeWindow) != 1 || !timeWindow %in% c("events", "seconds", "minutes", "hours", "days", "weeks", "months", "years")) { - timeWindow <- "events" - warning("The 'timeWindow' argument was invalid. Proceeding with 'timeWindow = \"events\" instead.") - } - - # wrap the vectors of exclude values for document variables into Java arrays - excludeAuthors <- .jarray(excludeAuthors) - excludeSources <- .jarray(excludeSources) - excludeSections <- .jarray(excludeSections) - excludeTypes <- .jarray(excludeTypes) - - # compile exclude variables and values vectors - dat <- matrix("", nrow = length(unlist(excludeValues)), ncol = 2) - count <- 0 - if (length(excludeValues) > 0) { - for (i in 1:length(excludeValues)) { - if (length(excludeValues[[i]]) > 0) { - for (j in 1:length(excludeValues[[i]])) { - count <- count + 1 - dat[count, 1] <- names(excludeValues)[i] - dat[count, 2] <- excludeValues[[i]][j] - } - } - } - var <- dat[, 1] - val <- dat[, 2] - } else { - var <- character() - val <- character() - } - var <- .jarray(var) # array of variable names of each excluded value - val <- .jarray(val) # array of values to be excluded - - # encode R NULL as Java null value if necessary - if (is.null(qualifier) || is.na(qualifier)) { - qualifier <- .jnull(class = "java/lang/String") - } - - # call rNetwork function to compute results - .jcall(dna_getHeadlessDna(), - "V", - "rTimeWindow", - networkType, - statementType, - variable1, - variable1Document, - variable2, - variable2Document, - qualifier, - qualifierDocument, - qualifierAggregation, - normalization, - TRUE, - duplicates, - start.date, - stop.date, - start.time, - stop.time, - timeWindow, - as.integer(windowSize), - kernel, - normalizeToOne, - indentTime, - var, - val, - excludeAuthors, - excludeSources, - excludeSections, - excludeTypes, - invertValues, - invertAuthors, - invertSources, - invertSections, - invertTypes - ) - exporter <- dna_getHeadlessDna()$getExporter() # save Java object reference to exporter class - - # compute distance matrix - if (distanceMethod == "modularity") { - stop("Differences in modularity have not been implemented yet. Please use absolute differences or spectral Euclidean distance as a distance method.") - } else if (!distanceMethod %in% c("absdiff", "spectral")) { - stop("Distance method not recognized. Try \"absdiff\" or \"spectral\".") - } - distance_mat <- .jcall(exporter, - "[[D", - "computeDistanceMatrix", - distanceMethod, - simplify = TRUE) - distance_mat <- distance_mat / max(distance_mat) # rescale between 0 and 1 - - # retrieve mid-point dates (gamma) - m <- .jcall(exporter, "[Lexport/Matrix;", "getMatrixResultsArray") # get list of Matrix objects from Exporter object - dates <- sapply(m, function(x) .jcall(x, "J", "getDateTimeLong")) # long integers, still needs conversion to date - - # define clustering function - hclustMethods <- c("single", "average", "complete", "ward") - cl <- function(method, distmat) { - tryCatch({ - similarity_mat <- 1 - distmat - g <- igraph::graph_from_adjacency_matrix(similarity_mat, mode = "undirected", weighted = TRUE, diag = FALSE) # graph needs to be based on similarity, not distance - if (method %in% hclustMethods) { - if (method == "single") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "single")) - } else if (method == "average") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "average")) - } else if (method == "complete") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "complete")) - } else if (method == "ward") { - suppressWarnings(cl <- stats::hclust(as.dist(distmat), method = "ward.D2")) - } - opt_k <- lapply(k.min:k.max, function(x) { - mem <- stats::cutree(cl, k = x) - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "kmeans") { - opt_k <- lapply(k.min:k.max, function(x) { - suppressWarnings(cl <- stats::kmeans(distmat, centers = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "pam") { - opt_k <- lapply(k.min:k.max, function(x) { - suppressWarnings(cl <- cluster::pam(distmat, k = x)) - mem <- cl$cluster - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(cl = cl, mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "spectral") { - sigma <- 1.0 - affinity_matrix <- exp(-distmat^2 / (2 * sigma^2)) - L <- diag(rowSums(affinity_matrix)) - affinity_matrix - D.sqrt.inv <- diag(1 / sqrt(rowSums(affinity_matrix))) - L.norm <- D.sqrt.inv %*% L %*% D.sqrt.inv - eigenvalues <- eigen(L.norm) # eigenvalue decomposition - opt_k <- lapply(k.min:k.max, function(x) { - U <- eigenvalues$vectors[, 1:x] - mem <- kmeans(U, centers = x)$cluster # cluster the eigenvectors - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } else if (method == "concor") { - suppressWarnings(mi <- stats::cor(similarity_mat)) - iter <- 1 - while (any(abs(mi) <= 0.999) & iter <= 50) { - mi[is.na(mi)] <- 0 - mi <- stats::cor(mi) - iter <- iter + 1 - } - mem <- ((mi[, 1] > 0) * 1) + 1 - } else if (method %in% igraphMethods) { - if (method == "fastgreedy") { - suppressWarnings(cl <- igraph::cluster_fast_greedy(g)) - } else if (method == "walktrap") { - suppressWarnings(cl <- igraph::cluster_walktrap(g)) - } else if (method == "leading_eigen") { - suppressWarnings(cl <- igraph::cluster_leading_eigen(g)) - } else if (method == "edge_betweenness") { - suppressWarnings(cl <- igraph::cluster_edge_betweenness(g)) - } else if (method == "spinglass") { - suppressWarnings(cl <- igraph::cluster_spinglass(g)) - } - opt_k <- lapply(k.min:k.max, function(x) { - mem <- igraph::cut_at(communities = cl, no = x) - mod <- igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem) - return(list(mem = mem, mod = mod)) - }) - mod <- sapply(opt_k, function(x) x$mod) - kk <- which.max(mod) - mem <- opt_k[[kk]]$mem - } - list(method = method, - modularity = igraph::modularity(x = g, weights = igraph::E(g)$weight, membership = mem), - memberships = mem) - }, - error = function(e) { - warning("Cluster method '", method, "' could not be computed due to an error: ", e) - }, - warning = function(w) { - warning("Cluster method '", method, "' threw a warning: ", w) - }) - } - - # apply all clustering methods to distance matrix - if (cores > 1) { - cat(paste("Clustering distance matrix on", cores, "cores.\n")) - a <- Sys.time() - l <- pbmcapply::pbmclapply(clusterMethods, cl, distmat = distance_mat, mc.cores = cores) - b <- Sys.time() - } else { - cat("Clustering distance matrix... ") - a <- Sys.time() - l <- lapply(clusterMethods, cl, distmat = distance_mat) - b <- Sys.time() - cat(intToUtf8(0x2714), "\n") - } - print(b - a) - for (i in length(l):1) { - if (length(l[[i]]) == 1) { - l <- l[-i] - clusterMethods <- clusterMethods[-i] - } - } - results <- list() - mod <- sapply(l, function(x) x$modularity) - best <- which(mod == max(mod))[1] - results$modularity <- mod[best] - results$clusterMethod <- clusterMethods[best] - - # temporal embedding via MDS - if (!requireNamespace("MASS", quietly = TRUE)) { - mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), - "state" = l[[best]]$memberships) - results$states <- mem - warning("Skipping temporal embedding because the 'MASS' package is not installed. Consider installing it.") - } else { - cat("Temporal embedding...\n") - a <- Sys.time() - distmat <- distance_mat + 1e-12 - mds <- MASS::isoMDS(distmat) # MDS of distance matrix - points <- mds$points - mem <- data.frame("date" = as.POSIXct(dates, tz = "UTC", origin = "1970-01-01"), - "state" = l[[best]]$memberships, - "X1" = points[, 1], - "X2" = points[, 2]) - results$states <- mem - b <- Sys.time() - print(b - a) - } - - results$distmat <- distance_mat - class(results) <- "dna_phaseTransitions" - attributes(results)$stress <- ifelse(ncol(results$states) == 2, NA, mds$stress) - attributes(results)$call <- mcall - return(results) -} - -#' Print the summary of a \code{dna_phaseTransitions} object -#' -#' Show details of a \code{dna_phaseTransitions} object. -#' -#' Print a summary of a \code{dna_phaseTransitions} object, which can be created -#' using the \link{dna_phaseTransitions} function. -#' -#' @param x A \code{dna_phaseTransitions} object. -#' @param ... Further options (currently not used). -#' -#' @author Philip Leifeld -#' -#' @rdname dna_phaseTransitions -#' @importFrom utils head -#' @export -print.dna_phaseTransitions <- function(x, ...) { - cat(paste0("States: ", max(x$states$state), ". Cluster method: ", x$clusterMethod, ". Modularity: ", round(x$modularity, 3), ".\n\n")) - print(utils::head(x$states, 20)) - cat(paste0("...", nrow(x$states), " further rows\n")) -} - -#' @rdname dna_phaseTransitions -#' @param object A \code{"dna_phaseTransitions"} object. -#' @param ... Additional arguments. Currently not in use. -#' @param plots The plots to include in the output list. Can be one or more of -#' the following: \code{"heatmap"}, \code{"silhouette"}, \code{"mds"}, -#' \code{"states"}. -#' -#' @author Philip Leifeld, Kristijan Garic -#' @importFrom ggplot2 autoplot ggplot aes geom_line geom_point xlab ylab -#' labs ggtitle theme_bw theme arrow unit scale_shape_manual element_text -#' scale_x_datetime scale_colour_manual guides -#' @importFrom rlang .data -#' @export -autoplot.dna_phaseTransitions <- function(object, ..., plots = c("heatmap", "silhouette", "mds", "states")) { - # settings for all plots - k <- max(object$states$state) - shapes <- c(21:25, 0:14)[1:k] - l <- list() - - # heatmap - if ("heatmap" %in% plots) { - try({ - if (!requireNamespace("heatmaply", quietly = TRUE)) { - warning("Heatmap skipped because the 'heatmaply' package is not installed.") - } else { - l[[length(l) + 1]] <- heatmaply::ggheatmap(1 - object$distmat, - dendrogram = "both", - showticklabels = FALSE, # remove axis labels - show_dendrogram = TRUE, - hide_colorbar = TRUE) - } - }) - } - - # silhouette plot - if ("silhouette" %in% plots) { - try({ - if (!requireNamespace("cluster", quietly = TRUE)) { - warning("Silhouette plot skipped because the 'cluster' package is not installed.") - } else if (!requireNamespace("factoextra", quietly = TRUE)) { - warning("Silhouette plot skipped because the 'factoextra' package is not installed.") - } else { - sil <- cluster::silhouette(object$states$state, dist(object$distmat)) - l[[length(l) + 1]] <- factoextra::fviz_silhouette(sil, print.summary = FALSE) + - ggplot2::ggtitle(paste0("Cluster silhouettes (mean width: ", round(mean(sil[, 3]), 3), ")")) + - ggplot2::ylab("Silhouette width") + - ggplot2::labs(fill = "State", color = "State") + - ggplot2::theme_classic() + - ggplot2::theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) - } - }) - } - - # temporal embedding - if ("mds" %in% plots) { - try({ - if (is.na(attributes(object)$stress)) { - warning("No temporal embedding found. Skipping this plot.") - } else if (!requireNamespace("igraph", quietly = TRUE)) { - warning("Temporal embedding plot skipped because the 'igraph' package is not installed.") - } else if (!requireNamespace("ggraph", quietly = TRUE)) { - warning("Temporal embedding plot skipped because the 'ggraph' package is not installed.") - } else { - nodes <- object$states - nodes$date <- as.character(nodes$date) - nodes$State <- as.factor(nodes$state) - - # Extract state values - state_values <- nodes$State - - edges <- data.frame(sender = as.character(object$states$date), - receiver = c(as.character(object$states$date[2:(nrow(object$states))]), "NA")) - edges <- edges[-nrow(edges), ] - g <- igraph::graph_from_data_frame(edges, directed = TRUE, vertices = nodes) - l[[length(l) + 1]] <- ggraph::ggraph(g, layout = "manual", x = igraph::V(g)$X1, y = igraph::V(g)$X2) + - ggraph::geom_edge_link(arrow = ggplot2::arrow(type = "closed", length = ggplot2::unit(2, "mm")), - start_cap = ggraph::circle(1, "mm"), - end_cap = ggraph::circle(2, "mm")) + - ggraph::geom_node_point(ggplot2::aes(shape = state_values, fill = state_values), size = 2) + - ggplot2::scale_shape_manual(values = shapes) + - ggplot2::ggtitle("Temporal embedding (MDS)") + - ggplot2::xlab("Dimension 1") + - ggplot2::ylab("Dimension 2") + - ggplot2::theme_bw() + - ggplot2::guides(size = "none") + - ggplot2::labs(shape = "State", fill = "State") - } - }) - } - - # state dynamics - if ("states" %in% plots) { - try({ - d <- data.frame( - time = object$states$date, - id = cumsum(c(TRUE, diff(object$states$state) != 0)), - State = factor(object$states$state, levels = 1:k, labels = paste("State", 1:k)), - time1 = as.Date(object$states$date) - ) - - # Extracting values - time_values <- d$time - state_values <- d$State - id_values <- d$id - - l[[length(l) + 1]] <- ggplot2::ggplot(d, ggplot2::aes(x = time_values, y = state_values, colour = state_values)) + - ggplot2::geom_line(aes(group = 1), linewidth = 2, color = "black", lineend = "square") + - ggplot2::geom_line(aes(group = id_values), linewidth = 2, lineend = "square") + - ggplot2::scale_x_datetime(date_labels = "%b %Y", breaks = "4 months") + # format x-axis as month year - ggplot2::xlab("Time") + - ggplot2::ylab("") + - ggplot2::ggtitle("State dynamics") + - ggplot2::theme_bw() + - ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + - ggplot2::guides(linewidth = "none") + - ggplot2::labs(color = "State") - }) - } - - return(l) } \ No newline at end of file From 5bbfb95dea4a65b8f98e1c81b7ab7fc361d701b0 Mon Sep 17 00:00:00 2001 From: leifeld Date: Tue, 20 Aug 2024 01:06:50 +0200 Subject: [PATCH 39/43] Adjusted release date in DNA and rDNA --- dna/src/main/java/dna/Dna.java | 2 +- rDNA/rDNA/DESCRIPTION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index ac7b2078..20202d8b 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,7 +17,7 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-07-22"; + public static final String date = "2024-08-20"; public static final String version = "3.0.11"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 30d9889d..103e06eb 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA Version: 3.0.11 -Date: 2024-08-19 +Date: 2024-08-20 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", From 8da39b8721f2bcd4e8809d0e4182e055f31612d0 Mon Sep 17 00:00:00 2001 From: leifeld Date: Tue, 20 Aug 2024 01:46:37 +0200 Subject: [PATCH 40/43] Updated gradle build tasks --- rDNA/build.gradle | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rDNA/build.gradle b/rDNA/build.gradle index 513400bb..a780e156 100644 --- a/rDNA/build.gradle +++ b/rDNA/build.gradle @@ -16,20 +16,15 @@ task rDNADocument { } } -// Task to find the most recent JAR file in the build directory -def findLatestJarFile() { - def jarDir = file("$rootDir/build") - def jarFiles = jarDir.listFiles({ file -> file.name.endsWith('.jar') } as FileFilter) - - if (jarFiles && jarFiles.size() > 0) { - return jarFiles.sort { -it.lastModified() }[0] - } else { - throw new GradleException("No jar files found in the build directory.") - } +// Task to copy R package sources to a temporary build directory +task copyRPackageSourcesToBuildDir(type: Copy, dependsOn: rDNADocument) { + from "$rootDir/rDNA/rDNA" + into "$rootDir/build/temp-rDNA" + exclude 'inst/java/**' } // Task to ensure the inst/java directory exists in the temporary build directory -task createJavaDirInTempBuildDir { +task createJavaDirInTempBuildDir (dependsOn: copyRPackageSourcesToBuildDir) { doLast { def javaDir = file("$rootDir/build/temp-rDNA/inst/java") if (!javaDir.exists()) { @@ -41,8 +36,20 @@ task createJavaDirInTempBuildDir { } } +// Task to find the most recent JAR file in the build directory +def findLatestJarFile() { + def jarDir = file("$rootDir/build") + def jarFiles = jarDir.listFiles({ file -> file.name.endsWith('.jar') } as FileFilter) + + if (jarFiles && jarFiles.size() > 0) { + return jarFiles.sort { -it.lastModified() }[0] + } else { + throw new GradleException("No jar files found in the build directory.") + } +} + // Task to copy the JAR file into the inst/java directory -task copyJarIntoTempBuildDir { +task copyJarIntoTempBuildDir (dependsOn: createJavaDirInTempBuildDir) { dependsOn ':dna:build', createJavaDirInTempBuildDir doLast { def latestJar = findLatestJarFile() @@ -57,15 +64,8 @@ task copyJarIntoTempBuildDir { } } -// Task to copy R package sources to a temporary build directory -task copyRPackageSourcesToBuildDir(type: Copy) { - from "$rootDir/rDNA/rDNA" - into "$rootDir/build/temp-rDNA" - exclude 'inst/java/**' -} - // Task to build the R package (create a .tar.gz file) and clean up after -task rDNABuild(dependsOn: [rDNADocument, copyRPackageSourcesToBuildDir, copyJarIntoTempBuildDir]) { +task rDNABuild(dependsOn: copyJarIntoTempBuildDir) { doLast { // Delete old .tar.gz files in the build directory def buildDir = file("$rootDir/build") From e03494bed7ca95025ecfe1969eba056effb414d8 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Tue, 20 Aug 2024 13:03:37 +0200 Subject: [PATCH 41/43] Updated README in repo --- README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e241a87e..e6f0edca 100755 --- a/README.md +++ b/README.md @@ -14,29 +14,29 @@ The Java software Discourse Network Analyzer (DNA) is a qualitative content anal [DNA 3.0](https://github.com/leifeld/dna/releases) was first released on 12 June 2022. It constitutes a major rewrite from the previous version DNA 2.0 beta 25. DNA 3 comes with many new features and improvements. The [release](https://github.com/leifeld/dna/releases) page contains all the details (scroll to version 3.0.7 for the first DNA 3 release). -[![DNA/rDNA build](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml/badge.svg)](https://github.com/leifeld/dna/actions/workflows/DNA%20build.yml) +[![build-check-test](https://github.com/leifeld/dna/actions/workflows/build-check-test.yml/badge.svg)](https://github.com/leifeld/dna/actions/workflows/build-check-test.yml) ## Installation of DNA -DNA was written in Java and requires Java 11 or higher on your system. You can check if you have Java >= 11 on your system by opening the terminal of your operating system (e.g., type `cmd` in your Windows start menu or use the terminal app on MacOS) and typing: +DNA was written in Java and requires Java 11 or higher on your system. You can check if you have Java >= 11 on your system by opening the terminal of your operating system (e.g., typing `cmd` in your Windows start menu or using the terminal app on MacOS) and typing: ``` shell java --version ``` -If this indicates a version below 11 (or 1.11), installing the latest version of [Adoptium Temurin OpenJDK](https://adoptium.net) is recommended before you proceed. Once installed, restart your system and check the version again. +If this indicates a version below 11 (or 1.11), installing the latest version of [Adoptium Temurin OpenJDK](https://adoptium.net) or any other Java >= 11 is recommended before you proceed. Once installed, restart your system and check the version again. To use DNA, simply download the latest `.jar` file under "[Releases](https://github.com/leifeld/dna/releases)" on the right along with the `sample.dna` file, a toy database you can load in DNA (password: `sample`) to play with the software. You can double-click on the `.jar` file to open DNA. No installation is required. MacOS users may need to give the program authorization to be executed. Some MacOS users reported issues with opening files. These issues should have been fixed in version 3.0.11. You can also browse the [issue tracker](https://github.com/leifeld/issues) (including closed issues) and the [commit messages](https://github.com/leifeld/dna/commits/master/) for more information on MacOS issues. Get in touch via the issue tracker or Matrix (infos below) if you are unable to solve these issues. -If your system does not want to start DNA with a double-click on the `.jar` file, you can also open it from the terminal. To do so, navigate to the directory where the `.jar` file is stored on your computer using the `cd` command, for example `cd ~/Documents/`. Once there, type in something like this, with `.jar` file corresponding to the file version you downloaded: +If your system does not want to start DNA with a double-click on the `.jar` file, you can also open it from the terminal. To do so, navigate to the directory where the `.jar` file is stored on your computer using the `cd` command, for example `cd ~/Documents/`. Once there, type in something like this, with the `.jar` file corresponding to the file version you downloaded: ``` shell -java -jar dna-3.0.10.jar +java -jar dna-3.0.11.jar ``` -If you require the latest (non-release) version of the DNA jar file from GitHub, you can clone the git repository to your computer and execute `./gradlew build` on your terminal or command line. This will build the jar file and store it in the `build/` directory of the cloned repository. Alternatively, you can try to download the latest artifact from the build process under [GitHub Actions](https://github.com/leifeld/dna/actions) by clicking on the latest build and scrolling down to "Artifacts". However, it is usually recommended to use the most recent [release](https://github.com/leifeld/dna/releases/) version. +If you require the latest (non-release) version of the DNA jar file from GitHub, you can clone the git repository to your computer and execute `./gradlew build` on your terminal or command line. This will build the jar file, the rDNA R package, and the bibliography, and store them in the `build/` directory of the cloned repository. If you only want to build the jar file, you can also execute `./gradlew :dna:build` (omit `./` on Windows). Alternatively, you can try to download the latest artifact from the build process under [GitHub Actions](https://github.com/leifeld/dna/actions) by clicking on the latest build and scrolling down to "Artifacts". However, it is usually recommended to use the most recent [release](https://github.com/leifeld/dna/releases/) version. Along with DNA, it is recommended to download [visone](https://visone.ethz.ch/), which can be opened in the same way as DNA. You can open `.graphml` files from DNA's network export in visone. @@ -44,7 +44,15 @@ Along with DNA, it is recommended to download [visone](https://visone.ethz.ch/), The R package rDNA connects DNA to R for data exchange and analysis. -Please note that the current version 3.0 does not have the full functionality of the old 2.0 version yet. It can create networks, but please use the old version for now if you require more complex data management and analysis functionality in R. It is possible to import DNA 2 data into DNA 3 at any point (but not the other way around). New R functions will be added in the future. +rDNA offers functionality such as: +- plotting networks with different layouts, +- applying many kinds of cluster analysis/community detection/subgroup analysis and selecting the best solution automatically to identify coalitions, +- detection of phase transitions, +- partitioning of concepts into backbones and redundant sets, +- ideological scaling via item response theory, +- barplots etc. + +The old 2.0 version additionally supported data exchange between DNA databases and rDNA by adding or retrieving documents, statements, attributes, statement types, coders etc. The 3.0 version does not support most of this data exchange functionality yet, so if you need to batch-import documents, for example, use DNA 2.0 first and then import everything into a DNA 3.0 database for further analysis (note that you cannot import from 3.0 into 2.0). More functions for data exchange and analysis will be added in the future. To install the new rDNA 3 directly from GitHub, try the following code in R: @@ -75,7 +83,7 @@ download.file(url = u, destfile = dest, mode = "wb") ## Documentation and community -- This **tutorial on YouTube** describes installation of DNA, basic data coding, network export, and network analysis using visone. The video clip is 18 minutes long. +- This **tutorial on YouTube** describes installation of DNA, basic data coding, network export, and network analysis using visone. The video clip is 18 minutes long and based on DNA 3.0.10. [![DNA tutorial](https://img.youtube.com/vi/u3hc86Tcs9A/0.jpg)](https://www.youtube.com/watch?v=u3hc86Tcs9A) From eaef4e88fa99ec64f34c69c1737a21a273f269e9 Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 26 Aug 2024 17:33:06 +0200 Subject: [PATCH 42/43] Fixed #294 (color picker in attribute manager) and a bug in the document batch importer GUI --- dna/src/main/java/dna/Dna.java | 4 +- dna/src/main/java/gui/AttributeManager.java | 6 +-- .../main/java/gui/DocumentBatchImporter.java | 37 +++++++++++++------ rDNA/rDNA/DESCRIPTION | 4 +- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/dna/src/main/java/dna/Dna.java b/dna/src/main/java/dna/Dna.java index 20202d8b..664a9fec 100644 --- a/dna/src/main/java/dna/Dna.java +++ b/dna/src/main/java/dna/Dna.java @@ -17,8 +17,8 @@ public class Dna { public static Dna dna; public static Logger logger; public static Sql sql; - public static final String date = "2024-08-20"; - public static final String version = "3.0.11"; + public static final String date = "2024-08-26"; + public static final String version = "3.0.12.1"; public static final String operatingSystem = System.getProperty("os.name"); public static File workingDirectory = null; public static Gui gui; diff --git a/dna/src/main/java/gui/AttributeManager.java b/dna/src/main/java/gui/AttributeManager.java index 36a1ce18..eca06395 100644 --- a/dna/src/main/java/gui/AttributeManager.java +++ b/dna/src/main/java/gui/AttributeManager.java @@ -635,8 +635,8 @@ public void setValueAt(Object aValue, int row, int col) { } } else if (col == 2) { // color try { - Dna.sql.setEntityColor(this.rows.get(row).getId(), (model.Color) aValue); - this.rows.get(row).setColor((model.Color) aValue); + Dna.sql.setEntityColor(this.rows.get(row).getId(), new model.Color(((Color) aValue).getRed(), ((Color) aValue).getGreen(), ((Color) aValue).getBlue())); + this.rows.get(row).setColor(new model.Color(((Color) aValue).getRed(), ((Color) aValue).getGreen(), ((Color) aValue).getBlue())); } catch (SQLException ex) { LogEvent l = new LogEvent(Logger.ERROR, "[SQL] Entity color could not be updated in the database.", @@ -921,7 +921,7 @@ private void changeColor(Color color) { * @param column The column index of the cell. */ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - changeColor((Color) value); + changeColor(((model.Color) value).toAWTColor()); panel.setBackground(UIManager.getColor("Table.selectionBackground")); return panel; } diff --git a/dna/src/main/java/gui/DocumentBatchImporter.java b/dna/src/main/java/gui/DocumentBatchImporter.java index 0e587026..eeb2a208 100644 --- a/dna/src/main/java/gui/DocumentBatchImporter.java +++ b/dna/src/main/java/gui/DocumentBatchImporter.java @@ -99,17 +99,32 @@ public void actionPerformed(ActionEvent e) { JButton browseButton = new JButton("Select text files", folderIcon); browseButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - FileChooser fc = new FileChooser(DocumentBatchImporter.this, "Select directory", false, "", "Import directory", true); - if (fc.getFiles() != null) { - files = fc.getFiles(); - listModel.clear(); - Arrays.stream(fc.getFiles()) - .filter(f -> f.isFile()) - .map(f -> f.getName()) - .forEachOrdered(f -> listModel.addElement(f)); - fileList.updateUI(); - if (files.length > 0) { - fileList.setSelectedIndex(0); + FileChooser fc = null; + try { + fc = new FileChooser(DocumentBatchImporter.this, "Select text files", false, "", "Import files", true); + } catch (NullPointerException npe) { + LogEvent l = new LogEvent(Logger.MESSAGE, + "[GUI] Cancelled file selection in batch import window.", + "Cancelled file selection in batch import window. No files were selected."); + Dna.logger.log(l); + } finally { + if (fc != null && fc.getFiles() != null) { + files = fc.getFiles(); + listModel.clear(); + Arrays.stream(fc.getFiles()) + .filter(f -> f.isFile()) + .map(f -> f.getName()) + .forEachOrdered(f -> listModel.addElement(f)); + fileList.updateUI(); + if (files.length > 0) { + fileList.setSelectedIndex(0); + } + } else { + // no files were selected or FileChooser was cancelled + LogEvent l = new LogEvent(Logger.MESSAGE, + "[GUI] No files were selected in the batch import window.", + "File selection was cancelled or no files were selected."); + Dna.logger.log(l); } } } diff --git a/rDNA/rDNA/DESCRIPTION b/rDNA/rDNA/DESCRIPTION index 103e06eb..a82898f7 100755 --- a/rDNA/rDNA/DESCRIPTION +++ b/rDNA/rDNA/DESCRIPTION @@ -1,6 +1,6 @@ Package: rDNA -Version: 3.0.11 -Date: 2024-08-20 +Version: 3.0.12.1 +Date: 2024-08-26 Title: Discourse Network Analysis in R Authors@R: c(person(given = "Philip", From a6ba5090ef988c7a431e5d3a75a7ce934c1a394d Mon Sep 17 00:00:00 2001 From: Philip Leifeld Date: Mon, 26 Aug 2024 18:22:04 +0200 Subject: [PATCH 43/43] Updated gradle build script by refining DNA version matcher --- dna/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dna/build.gradle b/dna/build.gradle index 77db1c00..7c6c3662 100644 --- a/dna/build.gradle +++ b/dna/build.gradle @@ -27,8 +27,8 @@ jar { // parse DNA version number and add to jar file name new File(project(':dna').projectDir.toString() + '/src/main/java/dna/Dna.java').eachLine { line -> if (line ==~ /.*String version.*/) { - def matches = (line =~ /\d+\.\d+\.\d+/) - archiveVersion = matches[0] + def matches = (line =~ /\d+\.\d+\.\d+(?:\.\d+)?/) + archiveVersion = matches[0].toString() } }