Skip to content

Commit

Permalink
Improve README and vignettes
Browse files Browse the repository at this point in the history
  • Loading branch information
juba committed Jun 25, 2021
1 parent e94d823 commit 7bdf5a1
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 38 deletions.
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ library(quanteda)
data_corpus_inaugural
```

First, we'll use `split_segments` to split each text in the corpus into segments of about 40 words (punctuation is taken into account) :
First, we'll use `split_segments` to split each document into segments of about 40 words (punctuation is taken into account) :

```r
corpus <- split_segments(data_corpus_inaugural, segment_size = 40)
Expand All @@ -61,7 +61,7 @@ dtm <- dfm(tok, tolower = TRUE)
dtm <- dfm_trim(dtm, min_docfreq = 10)
```

We can then apply a simple clustering on this dtm with the `rainette` function. We specify the number of clusters (`k`), the minimum size for a cluster to be splitted at next step (`min_split_members`) and the minimum number of forms in each segment (`min_segment_size`) :
We can then apply a simple clustering on this matrix with the `rainette` function. We specify the number of clusters (`k`), and the minimum number of forms in each segment (`min_segment_size`). Segments which do not include enough forms will be merged with the following or previous one when possible.

```r
res <- rainette(dtm, k = 6, min_segment_size = 15)
Expand Down Expand Up @@ -92,15 +92,15 @@ Or cut the tree at chosen `k` and add a group membership variable to our corpus
docvars(corpus)$cluster <- cutree(res, k = 5)
```

In addition to this, we can also perform a double clustering, *ie* two simple clusterings produced with different `min_segment_size` which are then "crossed" to generate more robust clusters. To do this, use `rainette2` on two `rainette` results :
In addition to this, we can also perform a double clustering, *ie* two simple clusterings produced with different `min_segment_size` which are then "crossed" to generate more robust clusters. To do this, we use `rainette2` on two `rainette` results :

```r
res1 <- rainette(dtm, k = 5, min_segment_size = 10, min_split_members = 10)
res2 <- rainette(dtm, k = 5, min_segment_size = 15, min_split_members = 10)
res <- rainette2(res1, res2, max_k = 5, min_members = 10)
```

You can then use `rainette2_explor`, `rainette2_plot` and `cutree` to explore and visualise the results.
We can then use `rainette2_explor` to explore and visualise the results.

```r
rainette2_explor(res, dtm, corpus)
Expand All @@ -110,14 +110,11 @@ rainette2_explor(res, dtm, corpus)

## Tell me more

Three vignettes are available, an introduction in english :
Three vignettes are available :

- [Introduction to rainette](https://juba.github.io/rainette/articles/introduction_en.html)

And an introduction and an algorithm description, in french :

- [Introduction à rainette](https://juba.github.io/rainette/articles/introduction_usage.html)
- [Description des algorithmes](https://juba.github.io/rainette/articles/algorithmes.html)
- An introduction in english : [Introduction to rainette](https://juba.github.io/rainette/articles/introduction_en.html)
- An introduction in french : [Introduction à rainette](https://juba.github.io/rainette/articles/introduction_usage.html)
- An algorithm description in french : [Description des algorithmes](https://juba.github.io/rainette/articles/algorithmes.html)

## Credits

Expand Down
22 changes: 10 additions & 12 deletions vignettes/introduction_en.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,19 @@ knitr::opts_chunk$set(echo = TRUE)

`rainette` is an R implementation of the Reinert textual clustering algorithm.
This algorithm is already available in softwares like
[Iramuteq](http://www.iramuteq.org/) or Alceste, the goal of `rainette` is
[Iramuteq](http://www.iramuteq.org/) or Alceste, the goal of `rainette` being
to provide it as an R package.

This is not a new algorithm, as the first articles describing it date back
to 1983. Here are some features of the method :
to 1983. Here are some characteristics of the method :

- each text is only assigned to one cluster, unlike methods like LDA.
- it is better suited for small "homogeneous" documents : for long texts the
- it is then better suited for small "homogeneous" documents : for long texts the
good practice is first to split them into segments.
- as the algorithm uses a singular value decomposition of a document-term
matrix, it may not work on very large corpus. In this case, as only the smaller
dimension of the matrix is taken into account for the decomposition, a
workaround can be to reduce the number of features by removing
the less frequent terms.
matrix, it is relatively fast but may not work on very large corpus. In this case,
as only the smaller dimension of the matrix is taken into account for the decomposition,
a workaround can be to reduce the number of features by removing the less frequent terms.

## Description of the algorithm

Expand All @@ -39,7 +38,7 @@ is to maximise the inter-cluster Chi-squared distance.

The algorithm is applied to the document-term matrix computed on the corpus.
Documents are called `uce` (elementary context units). If an `uce` doesn't
include enough terms, it can be merged with the following one into an `uc`
include enough terms, it can be merged with the following or previous one into an `uc`
(context unit). The resulting matrix is then weighted as binary, so only the
presence/absence of terms are taken into account, not their frequencies.

Expand Down Expand Up @@ -108,7 +107,7 @@ As explained before, as it doesn't take into account terms frequencies and
assign each document to only one cluster, the Reinert method must be applied
to short and "homogeneous" documents. It could be ok if you work on tweets or short
answers to a specific question, but on longer documents the corpus must first be split
into small textual segments.
into short textual segments.

The `split_segments` function does just that, and can be applied on a `tm` or
`quanteda` corpus.
Expand All @@ -123,7 +122,7 @@ corpus <- split_segments(data_corpus_inaugural, segment_size = 40)
```

`split_segments` will split the original texts into smaller chunks, attempting to
respect sentences and punctuation when possible. The function takes three
respect sentences and punctuation when possible. The function takes two
arguments :

- `segment_size` : the preferred segment size, in words
Expand Down Expand Up @@ -177,8 +176,7 @@ Here we will compute 5 clusters with a `min_segment_size` of 15 :
res <- rainette(dtm, k = 5, min_segment_size = 15)
```

To help exploring the clustering results, `rainette` offers a small *shiny
gadget* which can be launched with `rainette_explor` :
To help exploring the clustering results, `rainette` offers an interactive interface which can be launched with `rainette_explor` :

```{r eval = FALSE}
rainette_explor(res, dtm, corpus)
Expand Down
30 changes: 15 additions & 15 deletions vignettes/introduction_usage.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fichier <- system.file("extdata", "manifeste_pc.txt", package = "rainette")
corpus <- import_corpus_iramuteq(fichier)
```

On peut vérifier que notre corpus est bien constitué de 4 documents (l'introduction et les trois parties principales), et d'une variable de métadonnée :
On peut vérifier que notre corpus est bien constitué de quatre documents (l'introduction et les trois parties principales), et d'une variable de métadonnée :

```{r}
corpus
Expand All @@ -40,13 +40,13 @@ docvars(corpus)

## Découpage en segments

La méthode Reinert de classification s'applique en général à des segments de texte relativement courts (appelés *uce*, unités de contexte élémentaires), et non à des textes longs. Une première étape consiste donc à découper chaque texte du corpus en segments via la fonction `split_segments`. Ici on découpe en segments d'environ 40 mots (l'algorithme essaie de tenir compte de la ponctuation pour, par exemple, placer les césures entre des phrases ou au niveau d'une virgule).
La méthode Reinert de classification s'applique plutôt à des segments de texte relativement courts, et non à des textes longs. Une première étape consiste donc à découper chaque texte du corpus en segments via la fonction `split_segments`. Ici on découpe en segments d'environ 40 mots (l'algorithme essaie de tenir compte de la ponctuation pour, par exemple, placer les césures entre des phrases ou au niveau d'une virgule).

```{r message=FALSE}
corpus <- split_segments(corpus, segment_size = 40)
```

Notre corpus est désormais constitué de 318 segments et 2 variables de métadonnées :
Notre corpus est désormais constitué de 278 segments et 2 variables de métadonnées :

```{r}
corpus
Expand All @@ -67,9 +67,9 @@ as.character(corpus)[1:2]

## Calcul et traitement de la matrice termes-documents

L'étape suivante est de calculer la matrice termes-documents (*dtm*), grand tableau numérique avec les documents en lignes, les mots en colonnes, et le nombre d'occurrences de chaque mot dans chaque document comme valeurs.
L'étape suivante est de calculer la matrice termes-documents (*dtm*), grand tableau numérique avec les documents en lignes, les mots en colonnes, et comme valeurs le nombre d'occurrences de chaque mot dans chaque document.

Notre corpus étant au format `quanteda`, on va utiliser les fonction de cette extension.
Notre corpus étant au format `quanteda`, on va utiliser les fonctions de cette extension.

D'abord on calcule la *dtm* en convertissant le texte en minuscules, et en supprimant ponctuation, nombres, et les mots-outils français les plus courants :

Expand All @@ -92,7 +92,7 @@ De nombreux autres traitements seraient possibles, mais on se contentera de cett

Une fois notre matrice prête, on peut procéder à une première forme de classification : une classification descendante hiérarchique simple, calculée avec la fonction `rainette`. On va lui passer plusiurs arguments : le nombre maximal de classes souhaitées (`k = 5`) et le nombre minimal de termes pour qu'une classe soit découpée en deux à l'étape suivante de la classification (`min_split_members = 10`).

L'argument `min_segment_size`, lui, indique le nombre minimal de mots par segment. En effet, lors du calcul de la dtm, certaines formes (mots-outils, mots trop peu fréquents) ont été supprimées, nos segments peuvent donc varier en taille (entendue comme le nombre de mots encore présents). Avec `min_segment_size = 10`, les segments comportant moins de 10 formes sont regroupés avec le segment suivant (si possible) jusqu'à atteindre la taille minimale souhaitée.
L'argument `min_segment_size`, lui, indique le nombre minimal de mots par segment. En effet, lors du calcul de la dtm, certaines formes (mots-outils, mots trop peu fréquents) ont été supprimées, nos segments peuvent donc varier en taille (entendue comme le nombre de mots encore présents). Avec `min_segment_size = 10`, les segments comportant moins de 10 formes sont regroupés avec le segment suivant ou précédent (si possible) jusqu'à atteindre la taille minimale souhaitée.

```{r message=FALSE}
res <- rainette(dtm, k = 5, min_segment_size = 10, min_split_members = 10)
Expand All @@ -104,7 +104,7 @@ L'objet résultat ne nous dit pas grand chose en lui-même :
res
```

Pour faciliter l'exploration des résultats, `rainette` propose une interface interactive sous la forme d'un *gadget shiny*, qui peut être lancée avec la fonction `rainette_explor` :
Pour faciliter l'exploration des résultats, `rainette` propose une interface interactive qui peut être lancée avec la fonction `rainette_explor` :

```{r eval = FALSE}
rainette_explor(res, dtm, corpus)
Expand All @@ -114,15 +114,15 @@ L'interface devrait ressembler à quelque chose comme ça :

![](rainette_explor_plot_fr.png)

Il est possible de modifier le nombre de classes, la statistique utilisée dans les graphiques (spécificité, termes les plus fréquents), etc. Par défaut, les graphiques sous chaque classe vous indiquent les termes les plus caractéristiques du groupe positivement (en bleu) ou négativement (en rouge et si vous avez coché la case *Show negative values*).
Il est possible de modifier le nombre de classes, la statistique utilisée dans les graphiques (spécificité, termes les plus fréquents), etc. Par défaut, les graphiques sous chaque classe indiquent les termes les plus caractéristiques du groupe positivement (en bleu) ou négativement (en rouge et si vous avez coché la case *Show negative values*).

Cette interface vous permet d'expérimenter librement sur le nombre de classes et leur interprétation.

L'onglet *Cluster documents* permet à tout moment de visualiser les documents d'une classe. Vous pouvez également filtrer ces documents en saisissant un mot ou une expression régulière dans le champ *Filter by term* :

![](rainette_explor_docs_fr.png)

Dans l'onglet *Summary*, vous pouvez également cliquer sur le bouton *Get R code* pour obtenir le code R correspondant au graphique actuellement affiché, ainsi que la commande `cutree` qui vous permet de récupérer les groupes d'appartenance de chaque document du corpus, là aussi selon le nombre de groupes actuellement affichés.
Dans l'onglet *Summary*, vous pouvez également cliquer sur *Get R code* pour obtenir le code R correspondant au graphique actuellement affiché, ainsi que la commande `cutree` qui vous permet de récupérer les groupes d'appartenance de chaque document du corpus, là aussi selon le nombre de groupes actuellement affichés.

```{r eval=FALSE}
## Clustering description plot
Expand All @@ -132,14 +132,14 @@ rainette_plot(res, dtm, k = 5, type = "bar", n_terms = 20, free_scales = FALSE,
cutree(res, k = 5)
```

Vous pouvez par exemple utiliser l'appel de `cutree` pour ajouter comme nouvelle métadonnée du corpus le groupe d'appartenance pour la classification en 5 classes :
Vous pouvez par exemple utiliser l'appel de `cutree` pour ajouter comme nouvelle métadonnée du corpus le groupe d'appartenance de chaque segment pour la classification en 5 classes :

```{r}
corpus$groupe <- cutree(res, k = 5)
head(docvars(corpus))
```

Ici les classes ont attribuées aux segments, et non aux documents dans leur ensemble. La fonction `clusters_by_doc_table` permet d'afficher, pour chaque document, le nombre de segments de chaque groupe :
Ici les classes ont attribuées aux segments, et non aux documents dans leur ensemble. La fonction `clusters_by_doc_table` permet d'afficher, pour chaque document (ici chacune des quatre parties du texte), le nombre de segments de chaque groupe :

```{r}
clusters_by_doc_table(corpus, clust_var = "groupe")
Expand Down Expand Up @@ -182,17 +182,17 @@ L'autre manière est d'appeler directement `rainette2` sur notre matrice dtm, en
res <- rainette2(dtm, min_segment_size1 = 10, min_segment_size2 = 15, max_k = 7, min_members = 10)
```

L'objet résultat est un *tibble* contenant, pour chaque valeur de `k`, les partitions optimales trouvées et leurs caractéristiques. Là encore, une interface interactive est proposée pour visualiser et explorer ces résultats. Elle se lance via la fonction `rainette2_explor` :
L'objet résultat est contient, pour chaque valeur de `k`, les partitions optimales trouvées et leurs caractéristiques. Là encore, une interface interactive est proposée pour visualiser et explorer ces résultats. Elle se lance via la fonction `rainette2_explor` :

```{r eval=FALSE}
rainette2_explor(res, dtm)
rainette2_explor(res, dtm, corpus)
```

![](rainette2_explor_fr.png)

L'interface est très semblable à la précédente, sauf qu'il n'y a plus de dendrogramme mais à la place un diagramme en barre des effectifs des groupes. Soyez attentifs aux `NA`, qui représentent les segments non classés : contrairement à la classification simple, ils peuvent être assez nombreux ici.
L'interface est très semblable à la précédente, sauf qu'il n'y a plus de dendrogramme mais à la place un diagramme en barre des effectifs des groupes. Soyez attentifs aux `NA`, qui représentent les segments non classés : contrairement à la classification simple, ils peuvent ici être assez nombreux.

Si certains points n'ont pas été affecté à un groupe, vous pouvez utiliser `rainette2_complete_groups` pour les assigner au groupe le plus proche selon une méthode _k-nearest neighbors_ (avec k=1) :
Si certains segments n'ont pas été affecté à un groupe, vous pouvez utiliser `rainette2_complete_groups` pour les assigner au groupe le plus proche selon une méthode _k-nearest neighbors_ (avec k=1) :

```{r}
groupes <- cutree(res, k = 5)
Expand Down

0 comments on commit 7bdf5a1

Please sign in to comment.