# Extracting hashtags from text: Flattening in Query.jl

**URL:** https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456
**Category:** New to Julia
**Created:** [December 8, 2020, 1:11pm UTC](https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456 "2020-12-08T13:11:33Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![v-ji](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/v-ji/32/18923_2.png) [@v-ji](https://discourse.julialang.org/u/v-ji)
#### Post date: [December 8, 2020, 1:11pm UTC](https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456/1 "2020-12-08T13:11:33Z")

</div>

Hi everyone! I’m currently trying to use extract hashtags from text. While I’ve figured out a working solution, I can’t shake the feeling there’s a more elegant solution. I’m used to the [Tidyverse](https://www.tidyverse.org) in R, which is why I’m using [Queryverse](https://www.queryverse.org)/[Query.jl](https://github.com/queryverse/Query.jl).

In the end, each hashtag should be in its own row (“unnesting” in tidyverse). Here’s what I’ve got so far:

```julia
using Queryverse

df = DataFrame(text = ["This is the #best #thing #ever", "I #love #Julia"])

df_tags = df |>
@mutate(matches = collect(eachmatch(r"#[\wÄäÖöÜüß]+", _.text))) |>
@mutate(tags = map(x -> x.match, _.matches)) |>
@select(-:matches) |>
DataFrame

df_tags = flatten(df_tags, :tags)

```

These are my questions:

1. Is there a way to combine the two `@mutate` statements into one? In R, this was a single line: `mutate(tags = str_extract_all(text, '#[\\wÄäÖöÜüß]+')) %>%`
2. Is there a way to integrate the flattening into the piping sequence? I have tried something like `@map(x -> flatten(x, :tags), _)` or `@map(flatten(_, :tags))`, but neither work. In general words: How do I apply a non-Query.jl function to the whole dataframe?

I’d be really glad if anyone could help me get a better understanding of Julia.

---

<div class="post-metadata">

### Author: ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)
#### Post date: [December 8, 2020, 2:43pm UTC](https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456/2 "2020-12-08T14:43:54Z")

</div>

I can’t comment on the Queryverse stuff but as for the regex I believe doing `r"#\p{L}+"` would pick up a hash followed by any unicode characters. Unless the only non ascii characters allowed are `ÄäÖöÜüß`?

---

<div class="post-metadata">

### Author: ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)
#### Post date: [December 9, 2020, 8:43am UTC](https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456/3 "2020-12-09T08:43:16Z")

</div>

I don’t use Queryverse, but I guess you could put the flatten call in there by making another anonymous function out of it like `|> x -> flatten(x, :tags)`.

With regard to the `@mutate` calls, you can get your matches directly with `[x.match for x in eachmatch(r"#\p{L}+", _.text)]`, instead of collecting first and then extracting. If you come from R you might not know this list comprehension syntax yet.

Finally, this is how I would write this, just plain DataFrames.jl plus another helper package called Chain.jl, which allows to use any function in the pipe without making anonymous functions first (no matter whether the piped thing is the first, second, etc. argument), and without needing the `|>` symbol.

```julia
using DataFrames
using Chain

df = DataFrame(text = ["This is the #best #thing #ever", "I #love #Julia"])

get_tags(s) = [x.match for x in eachmatch(r"#\p{L}+", s)]

@chain df begin
    transform(:text => ByRow(get_tags) => :tags)
    flatten(:tags)
end

```

---

<div class="post-metadata">

### Author: ![v-ji](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/v-ji/32/18923_2.png) [@v-ji](https://discourse.julialang.org/u/v-ji)
#### Post date: [December 14, 2020, 10:31am UTC](https://discourse.julialang.org/t/extracting-hashtags-from-text-flattening-in-query-jl/51456/4 "2020-12-14T10:31:55Z")

</div>

Hi everyone!

Excuse the late reply, it just took some time for me to play around with the code. Thank you both for your input—I’d never seen that `\p{L}` syntax before and it’ll be very useful in the future.

Experimenting with your code @jules I discovered [DataFramesMeta.jl](https://github.com/JuliaData/DataFramesMeta.jl), which gives me a familiar grammar coming from R and, from what I’ve gathered, better integrates with the wider Julia ecosystem compared to Queryverse.jl. Splitting the `eachmatch` into its own function is a great tip and I’ll have a closer look at the list comprehension syntax.

This is what I ended up with, for reference:

```julia
using DataFrames
using DataFramesMeta

df = DataFrame(text = ["This is the #best #thing #ever", "I #love #Julia"])

get_tags(s) = [x.match for x in eachmatch(r"#\p{L}+", s)]

df_tags = @linq df |>
transform(tags = get_tags.(:text)) |>
flatten(:tags)

```

Thanks again!
